跨进程:Client进程只不过是持有了Server端的代理;代理对象协助驱动完成了跨进程通信。
对于一些需求,比如切换语言重启应用,如果项目中很多地方有通过Application.getResource().getString(int stringRes)
在部分手机可能存在语言没有切换的问题,比较简单的能解决这个问题的方式是,杀死当前进程,重新启动app。
具体实现:
1、子进程服务
/**
* 跨进程重启,避免用Application获取的文案,切换语言后文案未更新问题
*/
public class RestartService extends Service {
public static final int MSG_CONNECTED = 1;
public static final int MSG_KILL_APP = 2;
public static final int MSG_RESTART_APP = 3;
private String mPackageName;//要重启的app的包名
@Override
public IBinder onBind(Intent intent) {
if (intent != null) {
mPackageName = intent.getStringExtra(AppRestartControl.PACKAGE_BUNDLE_KEY);
}
Messenger restartMessenger = new Messenger(new ServerMsgHandler(this, mPackageName));
return restartMessenger.getBinder();
}
private static class ServerMsgHandler extends Handler {
private Context mContext;
private String mPackageName;
public ServerMsgHandler(Context context, String packageName) {
mContext = context;
mPackageName = packageName;
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_CONNECTED://收到app进程发来的消息,binder连接成功
try {
msg.replyTo.send(Message.obtain(null, MSG_KILL_APP));//通知app进程自杀
} catch (RemoteException e1) {
e1.printStackTrace();
}
break;
case MSG_RESTART_APP://收到app进程自杀前发出的重启消息
ThreadPoolFactory.instance().fixExecutor(new Runnable() {//避免子进程anr
@Override
public void run() {
try {
Logger.d("restart", "============准备重启");
Thread.sleep(1000);//延迟1秒,等app进程死透了再重启
Logger.d("restart", "============重启");
DeviceUtils.startApp(mContext, mPackageName);
Logger.d("restart", "============重启结束");
Thread.sleep(1000);//延迟1秒
Process.killProcess(Process.myPid());//重启进程自杀
} catch (Exception e) {
e.printStackTrace();
}
}
});
break;
default:
super.handleMessage(msg);
}
}
}
}
2、app进程中调用的api
/**
* app跨进程重启包装类
*/
public class AppRestartControl {
public static final String PACKAGE_BUNDLE_KEY = "package_bundle_key";
private Messenger mServerMsger = null;//用于包装从重启进程返回的binder的Messenger
private Context mContext;
public AppRestartControl(Context context) {
mContext = context;
}
public void doBindService(String packageName) {
ThreadPoolFactory.instance().fixExecutor(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(mContext, RestartService.class);
intent.putExtra(PACKAGE_BUNDLE_KEY, packageName);
mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
});
}
public void unBindService(Context context) {
context.unbindService(mConnection);
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
mServerMsger = new Messenger(service);//包装重进进程的Binder,通过该Messenger可以使当前进程与重启进程交互,使当前进程发送消息给重启进程
try {
Message msg = Message.obtain(null, RestartService.MSG_CONNECTED);
msg.replyTo = getClientMessenger();//消息的replyTo指向当前进程中创建的Messenger,方便接收重启进程发过来的消息
mServerMsger.send(msg);//消息发送给重启进程
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName className) {
mServerMsger = null;
}
};
/** 创建接收重启进程消息的Messenger */
private Messenger getClientMessenger() {
ClientMsgHandler handler = new ClientMsgHandler(mServerMsger);
Messenger clientMsger = new Messenger(handler);
handler.setClientMsger(clientMsger);
return clientMsger;
}
private static class ClientMsgHandler extends Handler {
private Messenger serverMsger;//用于包装从重启进程返回的binder的Messenger
private Messenger clientMsger;
public ClientMsgHandler(Messenger serverMsger) {
this.serverMsger = serverMsger;
}
public void setClientMsger(Messenger clientMsger) {
this.clientMsger = clientMsger;
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case RestartService.MSG_KILL_APP:
Message restartMsg = Message.obtain(null, RestartService.MSG_RESTART_APP);
restartMsg.replyTo = clientMsger;
try {
if (serverMsger != null) {
serverMsger.send(restartMsg);
}
Process.killProcess(Process.myPid());
System.exit(0);
} catch (Exception e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
}
3、在Manifest中注册重启服务,并标明子进程
<service android:name="xxx.restart.RestartService"
android:process=":restart"/>
4、调用方式
/**重启app*/
public static void rebootApp(final Context context) {
AppRestartControl restart = new AppRestartControl(context);
restart.doBindService(context.getPackageName());
}
public static void startApp(Context context,String packageName) throws Exception {
PackageInfo pi = context.getPackageManager().getPackageInfo(packageName, 0);
Intent resolveIntent = new Intent(Intent.ACTION_MAIN, null);
resolveIntent.addCategory(Intent.CATEGORY_LAUNCHER);
resolveIntent.setPackage(pi.packageName);
List<ResolveInfo> apps = context.getPackageManager().queryIntentActivities(resolveIntent, 0);
ResolveInfo ri = apps.iterator().next();
if (ri != null) {
String className = ri.activityInfo.name;
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ComponentName cn = new ComponentName(packageName, className);
intent.setComponent(cn);
context.startActivity(intent);
}
}