实现安卓程序退出后重进自己程序的一个小功能(android,unity)
来源:互联网 发布:新浪微博数据2017 编辑:程序博客网 时间:2024/05/10 11:23
本人是初来乍到的小菜鸟,可能说的有些地方不对,请批评指出,谢谢^v^
首先,我们要做的是安卓程序退出时能重新打开我们这个安卓程序,所以这些知识都涉及到安卓开发,但是我们所做的安卓项目,是由unity打包出来的,所以最开始要知道unity是如何跟安卓交互的。这里提供几篇博客教你如何用unity跟安卓交互:http://www.jianshu.com/p/7e46fe7485bb
http://blog.csdn.net/myarrow/article/details/46342371
你需要知道的就是,安卓调用unity的方式通过unity中的一个classes.jar,里面有一个UnityPlayer.UnitySendMessage方法,是安卓通知到unity的一个方法。
UnityPlayer.UnitySendMessage("GameObject", "Method", "arg");
三个参数,第一个参数是说要发给游戏场景中的哪一个游戏物体,第二个参数表示要调用游戏物体上挂载脚本的哪个方法,第三个参数表示调用者方法你要给他传递什么参数,三个参数都是string类型。
接着是unity调用安卓,这里就简单说一下,不多累赘,就是首先要将你的安卓场景Activity继承自UnityPlayerActivity,然后在Activity中写一些你自己需要被调用的方法,接着在unity中 写入这些代码
AndroidJavaClass jc = newAndroidJavaClass("com.unity3d.player.UnityPlayer"); AndroidJavaObject jo = jc.Get<AndroidJavaObject>("currentActivity"); jo.Call("方法名", "参数"); jo.CallStatic("方法名", "参数1","参数2");
call表示调用普通方法,CallStatic表示调用静态方法,参数是可以多个的。
接着我们需要理解的一个就是安卓应用Activity的一个生命周期,他的生命周期就跟我们unity的生命周期差不多,这里提供一个博客:http://www.cnblogs.com/bravestarrhu/archive/2012/05/02/2479461.html
这里的生命周期就是指的Activity的生命周期:
这些生命周期的函数,跟unity一样,是写在Activity中的,代码如下:
public class MainActivity extends UnityPlayerActivity { private static final String TAG = "MainActivity "; @Override protected void onCreate(Bundle savedInstanceState) { Log.i(TAG , "MainActivity onDestroy"); super.onCreate(savedInstanceState); } @Override protected void onDestroy(){ Log.i(TAG , "MainActivity onDestroy"); super.onDestroy(); }}
好了,接着我们进入正题,如何让安卓程序退出后重新打开我们的程序,第一步,我们要知道一个东西,就是安卓是如何打开另外一个应用的,这里提供几篇博客:
http://blog.csdn.net/zml_2015/article/details/50413597 (需要包名和Activity名)
http://blog.csdn.net/mad1989/article/details/38090513 (只需要包名)
简单说一下,我们要打开一个安卓程序,我们就必须知道对应安卓程序的包名(也就是unity项目中打包时的Bundle Identifier),然后还需要一个className,也就是我们的Activity的名字,不知道Activity名字也不要紧,看第二个博客,只通过包名打开一个应用。
private void doStartApplicationWithPackageName(String packagename) { // 通过包名获取此APP详细信息,包括Activities、services、versioncode、name等等 PackageInfo packageinfo = null; try { packageinfo = getPackageManager().getPackageInfo(packagename, 0); } catch (NameNotFoundException e) { e.printStackTrace(); } if (packageinfo == null) { return; } // 创建一个类别为CATEGORY_LAUNCHER的该包名的Intent Intent resolveIntent = new Intent(Intent.ACTION_MAIN, null); resolveIntent.addCategory(Intent.CATEGORY_LAUNCHER); resolveIntent.setPackage(packageinfo.packageName); // 通过getPackageManager()的queryIntentActivities方法遍历 List<ResolveInfo> resolveinfoList = getPackageManager() .queryIntentActivities(resolveIntent, 0); ResolveInfo resolveinfo = resolveinfoList.iterator().next(); if (resolveinfo != null) { // packagename = 参数packname String packageName = resolveinfo.activityInfo.packageName; // 这个就是我们要找的该APP的LAUNCHER的Activity[组织形式:packagename.mainActivityname] String className = resolveinfo.activityInfo.name; // LAUNCHER Intent Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); // 设置ComponentName参数1:packagename参数2:MainActivity路径 ComponentName cn = new ComponentName(packageName, className); intent.setComponent(cn); startActivity(intent); } }
好了,这就是通过包名打开另外一个应用的方法,你会发现,现在只是我们功能最开始的一个小实现,已经可以打开一个应用了,但是这仅仅只是开始。
接着呢,因为我们需要退出后打开自己这个应用,所以我们把这方法写在了上面的OnDestroy方法中,如下所示:
public class MainActivity extends UnityPlayerActivity { static boolean ReOpen = false;//定义一个变量,通过unity去改变这个变量 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override protected void onDestroy(){ if(ReOpen){ //通过包名打开你的项目 doStartApplicationWithPackageName("com.xxx.xxx"); } super.onDestroy(); } //这个方法需要再unity中去调用,然后退出时才会重新打开我们的项目,就是设置一个开关 public void ReOpen(){ //UnityPlayer.UnitySendMessage("Cube", "CallBack", "setReOpen"); ReOpen = true; } private void doStartApplicationWithPackageName(String packageName){ //打开项目的代码... }}
然后这样写完之后,你就会发现,这并没有什么用,因为,你一旦退出后,就会提示出一个xxx已停止运行,它已经报错了,为什么呢?因为你的这个打开操作时基于intent,你都已经杀死了这个而程序的进程了,他都不工作了,怎么可能再给你打开一个应用,所以只有在你Activity生命周期还活着的情况下,才能打开另外一个应用。那怎么办呢?
这时就需要用到我们安卓的四大组件之一——service(服务),这个service的用法,我提供几篇博客供学习:
http://blog.csdn.net/guolin_blog/article/details/11952435/
http://www.2cto.com/kf/201404/296058.html
http://www.cnblogs.com/bravestarrhu/archive/2012/05/02/2479461.html
用法跟Activity差不多,就是要继承自Service,然后最主要的就是需要再你的AndroidManifest.xml中去配置一下你的Service
代码:
public class ReOpenService extends Service { public static final String TAG = "ReOpenService"; @Override public void onCreate() { super.onCreate(); Log.d(TAG, "onCreate() executed"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand() executed"); //在这里写上你需要做的事情,我个人一般喜欢在这里创建一个线程Thread去持续执行一些东西 return super.onStartCommand(intent, flags, startId); } //可以写上你自己的方法,然后去调用它 private void yourMethod(){ //刚刚开启应用的方法,就可以写在这里 } @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy() executed"); } @Override public IBinder onBind(Intent intent) { return null; } }
然后是在AndroidManifest.xml去配置,这个配置要写在中
<application android:allowBackup="true" android:icon=" " android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!--在这里写上你的service的配置--> <service android:name="com.xxx.xxx.ReOpenService" ></service> </application>
配置好后,最后是启用service,我们需要再Activity中去启用它,在刚刚的onDestroy方法中启用,需要一个intent参数,这个参数需要两个类名参数,第一个是你的Activity类型,一个是你的service的类型
@Override protected void onDestroy(){ if(ReOpen){ Intent intent = new Intent(MainActivity.this,ReOpenService.class); startService(intent); } super.onDestroy(); }
这样就算是完成了,但是同样的,跑起来后,如果你多打印几个Log,会发现,我们的这个服务,一打开就会被杀死,老样子,还是会提示xxx程序已停止运行,也就是说,目前我们的这个service是基于我们这个程序的,程序一旦死亡,service也会结束。所以我们需要对这个service进行保活操作,就是防止这个service死亡,程序死了,但是要让service不死,这就需要对我们的service进行一个保活,也是安卓守护进程的一个实现。
这里提供几篇博客:
http://www.jianshu.com/p/b16631a2fe3c
https://www.zhihu.com/question/21859600
http://blog.csdn.net/liangxanhai/article/details/7752898(守护进程概念)
http://blog.csdn.net/xiaobaifeiji/article/details/51225063(保活)
http://blog.csdn.net/t12x3456/article/details/8982198(几种service的保活)
我的service最后实现的代码:
public class ReOpenService extends Service { private static Thread mThread; private static final String TAG = "ReOpenService"; // 守护进程 Service ID private final static int DAEMON_SERVICE_ID = 2; @Override public void onCreate() { Log.i(TAG, "ReOpenService-onCreate"); super.onCreate(); } @SuppressWarnings("deprecation") @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); Log.i(TAG, "ReOpenService-onStart"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { //执行文件的下载或者播放等操作 Log.i(TAG, "ReOpenService-onStartCommand:" + DAEMON_SERVICE_ID + PackageName); // 利用 Android 漏洞提高进程优先级, startForeground(DAEMON_SERVICE_ID, new Notification()); // 当 SDk 版本大于18时,需要通过内部 Service 类启动同样 id 的 Service if (Build.VERSION.SDK_INT >= 18) { Intent innerIntent = new Intent(this, DaemonInnerService.class); startService(innerIntent); } //执行我们需要执行的方法 TestMethod(); /* * 这里返回状态有三个值,分别是: * 1、START_STICKY:当服务进程在运行时被杀死,系统将会把它置为started状态,但是不保存其传递的Intent对象,之后,系统会尝试重新创建服务; * 2、START_NOT_STICKY:当服务进程在运行时被杀死,并且没有新的Intent对象传递过来的话,系统将会把它置为started状态, * 但是系统不会重新创建服务,直到startService(Intent intent)方法再次被调用; * 3、START_REDELIVER_INTENT:当服务进程在运行时被杀死,它将会在隔一段时间后自动创建,并且最后一个传递的Intent对象将会再次传递过来。 */ return START_STICKY; } //自己需要执行的方法 private void TestMethod() { Log.i(TAG, "test method"); if (mThread == null || !mThread.isAlive()) { Log.i(TAG, "create Thread"); mThread = new Thread(new Runnable() { @Override public void run() { try { //Thread.sleep(1000);原来想延时1秒,但是后来发现不需要了 //打开我们的包名的方法 doStartApplicationWithPackageName(); } catch (Exception e) { //这里最好这么写,因为这样可以看出你执行时报了什么错误,好进行修改 Log.e(TAG, "Exception Message : " + e.getMessage()); }finally{ StopServiceSelf(); } } }); mThread.start(); } } //service杀死自己的方法 private void StopServiceSelf(){ this.stopSelf(); } @Override public IBinder onBind(Intent intent) { Log.i(TAG, "ReOpenService-onBind"); return null; } @Override public void onDestroy() { Log.i(TAG, "ReOpenService-onDestroy"); super.onDestroy(); } private void doStartApplicationWithPackageName() { //获取我们自己这个项目的包名,也是AndroidManifest中的包名 String packagename = this.getPackageName(); Log.i(TAG, "ReOpenService-doStartApplication : "+packagename); // 通过包名获取此APP详细信息,包括Activities、services、versioncode、name等等 PackageInfo packageinfo = null; try { packageinfo = getPackageManager().getPackageInfo(packagename, 0); } catch (NameNotFoundException e) { e.printStackTrace(); } if (packageinfo == null) { return; } // 创建一个类别为CATEGORY_LAUNCHER的该包名的Intent Intent resolveIntent = new Intent(Intent.ACTION_MAIN, null); resolveIntent.addCategory(Intent.CATEGORY_LAUNCHER); resolveIntent.setPackage(packageinfo.packageName); // 通过getPackageManager()的queryIntentActivities方法遍历 List<ResolveInfo> resolveinfoList = getPackageManager() .queryIntentActivities(resolveIntent, 0); ResolveInfo resolveinfo = resolveinfoList.iterator().next(); if (resolveinfo != null) { // packagename = 参数packname String packageName = resolveinfo.activityInfo.packageName; // 这个就是我们要找的该APP的LAUNCHER的Activity[组织形式:packagename.mainActivityname] String className = resolveinfo.activityInfo.name; // LAUNCHER Intent Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); // 设置ComponentName参数1:packagename参数2:MainActivity路径 ComponentName cn = new ComponentName(packageName, className); intent.setComponent(cn); //这里需要添加的一个flag,不然那会报错,打不开应用 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); } } /** * 实现一个内部的 Service,实现让后台服务的优先级提高到前台服务,这里利用了 android 系统的漏洞, * 不保证所有系统可用,测试在7.1.1 之前大部分系统都是可以的,不排除个别厂商优化限制 */ public static class DaemonInnerService extends Service { @Override public void onCreate() { Log.i(TAG, "DaemonInnerService -> onCreate"); super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "DaemonInnerService -> onStartCommand"); startForeground(DAEMON_SERVICE_ID, new Notification()); stopSelf(); return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. throw new UnsupportedOperationException("onBind 未实现"); } @Override public void onDestroy() { Log.i(TAG, "DaemonInnerService -> onDestroy"); super.onDestroy(); } }}
关于上面代码,最后需要提几点:
1、守护进程需要一个serviceID,然后设置startForeground,这样保证我们的额service的优先级比较高,不容易被杀死,接着在onStartCommand中返回值写成START_STICKY,这样可以保证service在死亡时可以再次被激活。
2、我们这样保活后的service,已经不依赖于我们的Activity了,所以我们项目推出后,service还是会正常跑起来的,而不会呗异常中断,所以我们在调用打开应用时,需要添加一个叫做FLAG_ACTIVITY_NEW_TASK的flag,不然会报错
提供几篇博客:
http://blog.csdn.net/dct8888/article/details/52064160
http://www.cnblogs.com/xiaoQLu/archive/2012/07/17/2595294.html
http://blog.csdn.net/debuglog/article/details/7249444
添加flag的代码如下:
Intent intent = new Intent(Intent.ACTION_MAIN); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3、关于如何获取包名,因为我们要打开自己这个应用,所以需要获取包名的操作,关于这个操作,提供几篇博客,有助于学习安卓中的一些可获取的信息类:
http://www.cnblogs.com/fly_binbin/archive/2012/08/17/2644119.html
http://blog.csdn.net/ddiagw/article/details/41308621
http://blog.csdn.net/true100/article/details/51028468
代码:
String packagename = this.getPackageName();
这样就算是结束了,最后,希望这篇博客能帮助到你!
***********以下为2017/8/18修改添加************
最后的最后=。=我给个提醒,该方法是针对机型的,有些机型(比如小米),好像是不支持这样启动一个service,就会导致,有的机型可以正常启动,有的机型启动不起来,所以后来,我朋友给我推荐了一个stackoverflow博客,歪果仁写的,实现自启动方法,而且不再看机型,代码就几行,完全不需要开启service,下面分享给大家:
博客:https://stackoverflow.com/questions/15564614/how-to-restart-an-android-application-programmatically
代码:
Intent i = getBaseContext().getPackageManager() .getLaunchIntentForPackage( getBaseContext().getPackageName() ); i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(i);
只需要在OnDestroy时调用就可以了:
@Override protected void onDestroy(){ Log.i("ReOpenService", "MainActivity onDestroy"); if(ReOpen){ ReStart(); } super.onDestroy(); } private void ReStart() { Log.i("ReOpenService", "ReStart Game!"); Intent i = getBaseContext().getPackageManager() .getLaunchIntentForPackage( getBaseContext().getPackageName() ); i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(i); }
- 实现安卓程序退出后重进自己程序的一个小功能(android,unity)
- 安卓如何实现点击一个按钮退出整个程序?
- 自己写的一个Android小程序
- Android程序的退出功能
- Android程序的退出功能
- 自己实现atoi函数功能,另加一个str2num有用的小程序
- 安卓退出程序
- 安卓开发如何优雅的实现退出整个程序
- android多activity退出整个程序的一个实现
- android多activity退出整个程序的一个实现
- android多activity退出整个程序的一个实现
- 【RichieZhu】Android多activity退出整个程序的一个实现
- android多activity退出整个程序的一个实现
- android多activity退出整个程序的一个实现
- 安卓程序退出的讨论
- android按back键退出程序时,实现“再按一次退出”的功能
- unity 如何实现安卓Android的toast功能
- Java 小程序:实现一个购物流程的功能
- 关于android 的事件分发机制
- 数组的交集、并集……
- SQL CONSTRAINT
- let和const命令学习总结
- Android App 启动页(Splash)黑/白闪屏现象产生原因与解决办法
- 实现安卓程序退出后重进自己程序的一个小功能(android,unity)
- 详解location.href几种用法的区别
- jQuery插件封装
- mint-ui —— search的使用
- MacOS使用命令行上传本地代码包到Github方法
- iOS开发多线程创建及线程间通信
- 为什么不读顶级会议论文
- Android四大组件-Service
- 【Java && Kotlin】泛型还有30秒达到战场,碾碎它