前言:
好久沒(méi)更新文章了成艘,距離上次更新已經(jīng)有5個(gè)月時(shí)間了赏半,前段時(shí)間結(jié)合自己在處理的權(quán)限方面的問(wèn)題寫了這么篇水文,希望能幫到其他人淆两。同時(shí)還是比較佩服那些高產(chǎn)的作者断箫,在兼顧工作的同時(shí)還能持續(xù)輸出優(yōu)秀的技術(shù)文章。
感慨下秋冰,回到正題仲义,本文主要要說(shuō)的是在權(quán)限請(qǐng)求體驗(yàn)上的一些優(yōu)化,并非權(quán)限適配方面的文章剑勾,角度上和目前市面上的權(quán)限文章可能不太一樣埃撵。
文章主要是針對(duì)目前公司app在權(quán)限請(qǐng)求使用上的一些改進(jìn)與優(yōu)化,大致可以分為三個(gè)部分
(1)權(quán)限庫(kù)權(quán)限回調(diào)bug修復(fù)
(2)系統(tǒng)權(quán)限請(qǐng)求彈框重疊解決
(3)系統(tǒng)權(quán)限開(kāi)關(guān)引起的app進(jìn)程殺死重啟
(1)權(quán)限庫(kù)權(quán)限請(qǐng)求回調(diào)
其實(shí)目前市面上有不少高質(zhì)量的權(quán)限請(qǐng)求庫(kù)可以使用虽另,幾行代碼就能幫助我們完成權(quán)限的請(qǐng)求暂刘,但說(shuō)來(lái)比較有意思,不管是我目前公司還是之前公司對(duì)于權(quán)限請(qǐng)求這塊的處理洲赵,似乎更加偏向于自己去實(shí)現(xiàn)類似的庫(kù)鸳惯。
其實(shí)不管是自己造輪子還是使用三方開(kāi)源庫(kù)商蕴,只要能做到對(duì)于其中的原理掌握都是可取的,使用三方庫(kù)更多的優(yōu)勢(shì)在于敏捷開(kāi)發(fā)芝发,節(jié)約時(shí)間成本绪商,但是使用三方庫(kù)一個(gè)比較明顯的缺點(diǎn)就是可定制化方面確實(shí)弱了點(diǎn),尤其是在你沒(méi)有完全摸清開(kāi)源庫(kù)代碼的情況下辅鲸,貿(mào)然的修改可能會(huì)引起各種問(wèn)題格郁。
扯的有點(diǎn)跑題了,回到權(quán)限問(wèn)題上來(lái)独悴,如果是自己去實(shí)現(xiàn)一個(gè)權(quán)限庫(kù)例书,其中一個(gè)問(wèn)題肯定是繞不開(kāi)的,那就是權(quán)限請(qǐng)求結(jié)果回調(diào)如何處理的問(wèn)題刻炒。Activity通過(guò)
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
方法處理權(quán)限請(qǐng)求的結(jié)果决采,那么問(wèn)題來(lái)了實(shí)現(xiàn)權(quán)限庫(kù)必然需要在
<font color=red size=50>onRequestPermissionsResult</font>
去處理請(qǐng)求,然后將處理結(jié)果通過(guò)callback的形式進(jìn)行回調(diào)坟奥。之前大致研究過(guò)個(gè)別開(kāi)源權(quán)限庫(kù)树瞭,主要做法大概有三種
(1)在項(xiàng)目的BaseActivity中復(fù)寫onRequestPermissionsResult方法,通過(guò)調(diào)用權(quán)限庫(kù)的處理方法實(shí)現(xiàn)回調(diào)爱谁,類似
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
PermissionManager.getInstance(this).onRequestPermissionsResult(requestCode,permissions,grantResults);
}
這種方案簡(jiǎn)單有效晒喷,也是最容易想到的方案,但是對(duì)于一個(gè)權(quán)限庫(kù)访敌,還需要入侵到BaseActivity中去做一些事情凉敲,個(gè)人覺(jué)得多少差點(diǎn)意思,你都是一個(gè)成熟的權(quán)限庫(kù)寺旺,要時(shí)候?qū)W會(huì)自己處理權(quán)限回調(diào)問(wèn)題了
(2)開(kāi)啟一個(gè)透明的activity爷抓,然后在該activity中得到回調(diào)的結(jié)果,通過(guò)PermissionManager傳入的callback實(shí)現(xiàn)回調(diào)這種方案也是比較容易想到的阻塑,也是一種可行的方案废赞,目前公司就是采用這種方案,但是同事在實(shí)現(xiàn)上存在一個(gè)bug叮姑,這個(gè)等下說(shuō)到
(3)通過(guò)給Activity添加一個(gè)fragment來(lái)實(shí)現(xiàn)權(quán)限結(jié)果的回調(diào)
這個(gè)方案是我認(rèn)為最為巧妙的實(shí)現(xiàn),通過(guò)fragment上同名的方法對(duì)回調(diào)進(jìn)行處理据悔。這其實(shí)就是對(duì)于fragment的巧妙利用传透,jetpack在實(shí)現(xiàn)liftcycle也是使用的類似的技巧。感興趣的可以自行搜索相關(guān)文章极颓。
回到文章說(shuō)的第二種實(shí)現(xiàn)上朱盐,一般我們的權(quán)限請(qǐng)求使用方式都是類似這種形式(不包括利用apt實(shí)現(xiàn)的注解形式)
PermissionManager.getInstance(context,permissionItems).checkPermission(new PermissionCallback() {
@Override
public void onGuaranteed(String permission) {
...
}
@Override
public void onDenied(String permission, boolean shouldShowAgain) {
...
}
@Override
public void onFinished(boolean isAllGuaranteed) {
...
}
});
如果是開(kāi)啟透明Activity處理權(quán)限請(qǐng)求,必須將permissionCallback傳遞給透明Activity菠隆,公司內(nèi)部權(quán)限庫(kù)在處理這塊時(shí)兵琳,采用了簡(jiǎn)單粗暴的方式
···
RequestPermissionActivity.setCallback(permissionCallback)
···
RequestPermissionActivity就是文章所說(shuō)的透明Activity狂秘,通過(guò)靜態(tài)方法將permissionCallback保存為Activity的靜態(tài)變量,然后在Activity銷毀的時(shí)候調(diào)用
@Override
protected void onDestroy() {
super.onDestroy();
permissionCallback = null;
}
避免內(nèi)存的泄露問(wèn)題躯肌,看起來(lái)簡(jiǎn)單有效者春,但是作為一個(gè)static類型的callback,當(dāng)同時(shí)單獨(dú)發(fā)起兩個(gè)權(quán)限請(qǐng)求就會(huì)導(dǎo)致后一個(gè)callback覆蓋掉前一個(gè)callback清女。這種場(chǎng)景在目前公司的業(yè)務(wù)環(huán)境是有可能出現(xiàn)的钱烟,比如最常見(jiàn)的在賬號(hào)登錄之后會(huì),由于業(yè)務(wù)關(guān)系會(huì)發(fā)起一次數(shù)據(jù)校驗(yàn)請(qǐng)求嫡丙,等到校驗(yàn)正確后才會(huì)發(fā)起定位權(quán)限請(qǐng)求拴袭,如果網(wǎng)絡(luò)存在延遲的情況下在數(shù)據(jù)返回之前直接切換到其他需要權(quán)限的頁(yè)面就會(huì)導(dǎo)致callback覆蓋問(wèn)題。
問(wèn)題說(shuō)明白之后剩下的解決思路就清晰了曙博,禁止將callback設(shè)置為activity的靜態(tài)變量拥刻,通過(guò)LocalBroadcastManager來(lái)實(shí)現(xiàn)透明Activity和PermissionManager之間的通信,解決方式參考如下
PermissionManager部分相關(guān)代碼:
private void startRequest(PermissionCallback callback) {
//給PermissionManager注冊(cè)一個(gè)本地廣播
InnerBroadcastReceiver receiver = new InnerBroadcastReceiver(callback, mId);
LocalBroadcastManager.getInstance(mContext)
.registerReceiver(receiver, new IntentFilter(PERMISSION_CALLBACK));
Intent intent = new Intent(mContext, RequestPermissionActivity.class);
...
intent.putExtra(RequestPermissionActivity.INTENT_KEY_DATA, permissionParcelable);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
...
}
private static class InnerBroadcastReceiver extends BroadcastReceiver {
PermissionCallback callback;
int mId;
InnerBroadcastReceiver(PermissionCallback callback, int id) {
this.callback = callback;
this.mId = id;
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent == null) {
return;
}
int id = intent.getIntExtra(ID, -1);
if (id != mId) {
return;
}
boolean unregister = intent.getBooleanExtra(UNREGISTER, false);
//Activity銷毀時(shí)需要及時(shí)注銷掉對(duì)應(yīng)廣播父泳,防止內(nèi)存泄露
if (unregister) {
LocalBroadcastManager.getInstance(context).unregisterReceiver(this);
} else if (callback != null) {
boolean finish = intent.getBooleanExtra(CHECK_FINISH, false);
if (finish) {
boolean isAllGuaranteed = intent.getBooleanExtra(IS_ALL_GUARANTEED, false);
callback.onFinished(isAllGuaranteed);
} else {
boolean approve = intent.getBooleanExtra(APPROVE, false);
String name = intent.getStringExtra(PERMISSION_NAME);
boolean shouldShowAgain = intent.getBooleanExtra(SHOULD_SHOW_AGAIN, true);
if (approve) {
//將透明Activity中的callback移到InnerBroadcastReceiver內(nèi)部
callback.onGuaranteed(name);
} else {
callback.onDenied(name, shouldShowAgain);
}
}
}
}
}
省略了部分和權(quán)限庫(kù)具體實(shí)現(xiàn)相關(guān)的代碼般哼,不影響整體思路的理解。在startRequest發(fā)起權(quán)限請(qǐng)求時(shí)先注冊(cè)一個(gè)本地廣播尘吗,廣播的接收在onReceive中處理逝她,主要就是分析返回的數(shù)據(jù)來(lái)決定調(diào)用onGuaranteed還是onDenied。本地廣播被注冊(cè)成功后睬捶,必須有一個(gè)廣播的發(fā)送者黔宛,透明Activity就是這么一個(gè)角色。
透明Activity部分代碼如下:
private void sendPermissionSignal(String permission, boolean approve, boolean shouldShowAgain) {
Intent intent = new Intent();
intent.setAction(PERMISSION_CALLBACK);
intent.putExtra(ID, mId);
intent.putExtra(APPROVE, approve);
intent.putExtra(PERMISSION_NAME, permission);
intent.putExtra(SHOULD_SHOW_AGAIN, shouldShowAgain);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
private void onGuarantee(String permission) {
sendPermissionSignal(permission, true, true);
}
private void onDeny(String permission, boolean shouldShowAgain) {
sendPermissionSignal(permission, false, shouldShowAgain);
}
@Override
protected void onDestroy() {
super.onDestroy();
Intent intent = new Intent();
intent.putExtra(UNREGISTER, true);
intent.putExtra(ID, mId);
intent.setAction(PERMISSION_CALLBACK);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
在透明Activity中權(quán)限請(qǐng)求的結(jié)果通過(guò)sendPermissionSignal通知到PermissionManager擒贸,同時(shí)在onDestroy時(shí)反注冊(cè)掉對(duì)應(yīng)的本地廣播臀晃,經(jīng)上述代碼處理后即可解決callback覆蓋問(wèn)題。
(2)系統(tǒng)權(quán)限請(qǐng)求框重疊問(wèn)題
直接上圖秒懂
可以看出出現(xiàn)了兩個(gè)系統(tǒng)權(quán)限框重疊的現(xiàn)象介劫,當(dāng)同時(shí)有兩個(gè)權(quán)限單獨(dú)發(fā)起權(quán)限請(qǐng)求時(shí)就會(huì)導(dǎo)致這種情況徽惋,比如兩個(gè)不同的組件模塊同時(shí)發(fā)起權(quán)限請(qǐng)求,雖然不影響app的正常運(yùn)行座韵,但看到這種彈框重疊總覺(jué)得app顯得有點(diǎn)low故源,作為一個(gè)有追求的開(kāi)發(fā)者肯定要解決掉這個(gè)問(wèn)題讳癌,主要思路就是通過(guò)一個(gè)管理類來(lái)統(tǒng)一調(diào)用app中的所有權(quán)限,類似于AsyncTask中默認(rèn)串行執(zhí)行任務(wù)行為,只有當(dāng)一個(gè)任務(wù)執(zhí)行完畢之后才會(huì)繼續(xù)執(zhí)行下一個(gè)任務(wù)楣责。
具體關(guān)于PermissionDispatcher實(shí)現(xiàn)如下:
public class PermissionDispatcher {
private volatile static PermissionDispatcher dispatcher;
private List<Task> tasks;//每次權(quán)限請(qǐng)求封裝成一個(gè)task
private Handler handler;
private Context context;
private PermissionDispatcher(Context context) {
handler = new Handler(Looper.getMainLooper());//UI線程執(zhí)行任務(wù)垦缅,避免多線程并發(fā)可能導(dǎo)致的問(wèn)題
tasks = new LinkedList<>();
this.context = context.getApplicationContext();
}
public static PermissionDispatcher getInstance(Context context) {
if (dispatcher == null) {
synchronized (PermissionDispatcher.class) {
if (dispatcher == null) {
dispatcher = new PermissionDispatcher(context);
}
}
}
return dispatcher;
}
public void checkPermissions(final boolean showCustomDialog, final boolean autoNext, final List<PermissionItem> items, final PermissionCallback callback) {
handler.post(new Runnable() {
@Override
public void run() {
PermissionDispatcher.Task task = new PermissionDispatcher.Task(items, showCustomDialog, callback, autoNext);
tasks.add(task);
if (tasks.size() == 1) {
PermissionDispatcher.Task current = tasks.get(0);
current.execute();
}
}
});
}
public void checkPermissions(final boolean showCustomDialog, final List<PermissionItem> items, final PermissionCallback callback) {
checkPermissions(showCustomDialog, true, items, callback);
}
public void checkPermission(final boolean showCustomDialog, boolean autoNext, final PermissionItem item, final PermissionCallback callback) {
List<PermissionItem> list = Collections.singletonList(item);
checkPermissions(showCustomDialog, autoNext, list, callback);
}
public void checkPermission(final boolean showCustomDialog, final PermissionItem item, final PermissionCallback callback) {
List<PermissionItem> list = Collections.singletonList(item);
checkPermissions(showCustomDialog, true, list, callback);
}
private class Task {
private List<PermissionItem> items;
private PermissionCallback callback;
private boolean showCustomDialog;
private boolean complete; //表示task是否執(zhí)行完畢
private boolean autoNext;
private Task(List<PermissionItem> items, boolean showCustomDialog
, PermissionCallback callback, boolean autoNext) {
this.items = items;
this.showCustomDialog = showCustomDialog;
this.callback = callback;
this.autoNext = autoNext;
}
private void execute() {
PermissionManager instance = PermissionManager.getInstance(context, showCustomDialog);
if (items != null && !items.isEmpty()) {
for (PermissionItem item : items) {
instance.addPermission(item);
}
}
instance.checkPermission(new PermissionCallback() {
@Override
public void onGuaranteed(String permission) {
complete = true;
callback.onGuaranteed(permission);
}
@Override
public void onDenied(String permission, boolean shouldShowAgain) {
// Log.e("mandy", "dispatcher onDenied");
complete = true;
callback.onDenied(permission, shouldShowAgain);
}
@Override
public void onFinished(boolean isAllGuaranteed) {
// Log.e("mandy", "onFinished???");
callback.onFinished(isAllGuaranteed);
if (autoNext) {
nextInternal();
}
}
});
}
}
private void nextInternal() {
if (tasks.isEmpty()) {
return;
}
tasks.remove(0);
if (!tasks.isEmpty()) {
PermissionDispatcher.Task task = tasks.get(0);
task.execute();
}
}
public void next() {
if (tasks.isEmpty()) {
return;
}
PermissionDispatcher.Task current = tasks.get(0);
if (current.autoNext) {
return;
}
if (current.complete) {
nextInternal();
}
}
}
具體的實(shí)現(xiàn)就是上述代碼了确憨,當(dāng)每次發(fā)起權(quán)限請(qǐng)求時(shí)都會(huì)封裝成task国撵,將該task保存到tasks容器內(nèi)部,當(dāng)task是容器中的第一個(gè)元素時(shí)會(huì)去執(zhí)行task的excute方法成黄,如果不是第一個(gè)元素則等待被調(diào)用呐芥。當(dāng)task執(zhí)行完畢之后就會(huì)從容器中被移除然后繼續(xù)執(zhí)行下一個(gè)task逻杖。
唯一需要特別說(shuō)明的就是autoNext這個(gè)變量,一般情況下當(dāng)一個(gè)task執(zhí)行完畢后會(huì)自動(dòng)繼續(xù)執(zhí)行下一個(gè)task這種情況下autoNext為true思瘟,但當(dāng)你請(qǐng)求的權(quán)限會(huì)調(diào)用起系統(tǒng)的一些頁(yè)面時(shí)情況會(huì)復(fù)雜一些荸百,比如拍照請(qǐng)求攝像頭權(quán)限成功后手機(jī)會(huì)調(diào)用系統(tǒng)相機(jī)頁(yè)面,如果這時(shí)PermissionDispatcher中還有未執(zhí)行的task潮太,當(dāng)該task被取出執(zhí)行時(shí)就會(huì)導(dǎo)致出現(xiàn)權(quán)限彈框覆蓋系統(tǒng)頁(yè)面的問(wèn)題管搪,如圖用戶正在拍著照,突然畫面中間彈個(gè)一個(gè)權(quán)限請(qǐng)求铡买,是不是顯得有點(diǎn)突兀更鲁,在使用體驗(yàn)上是不是略差。理解上述場(chǎng)景之后就可以明白這種情況下就不能將autoNext設(shè)置為true奇钞,需要等拍照結(jié)束調(diào)用onActivityResult之后再繼續(xù)執(zhí)行PermissionDispatcher中剩余的task澡为,這就是autoNext字段的具體作用。當(dāng)autoNext設(shè)置為false時(shí)就不會(huì)主動(dòng)調(diào)用下一個(gè)需要執(zhí)行的task景埃,這時(shí)候就需要通過(guò)手動(dòng)調(diào)用PermissionDispatcher的next方法觸發(fā)下一個(gè)task的執(zhí)行媒至。
需要特別說(shuō)明下的是,這個(gè)權(quán)限請(qǐng)求彈框覆蓋系統(tǒng)頁(yè)面問(wèn)題不是引入PermissionDispatcher才導(dǎo)致的谷徙,一般的app如果沒(méi)做特殊處理都是有可能存在這個(gè)問(wèn)題的拒啰。
(3)系統(tǒng)權(quán)限開(kāi)關(guān)導(dǎo)致的app進(jìn)程殺死問(wèn)題
這也是權(quán)限請(qǐng)求處理過(guò)程中避不開(kāi)的一個(gè)坑,復(fù)現(xiàn)場(chǎng)景很簡(jiǎn)單當(dāng)你在手機(jī)系統(tǒng)設(shè)置頁(yè)面關(guān)閉app中某一個(gè)權(quán)限時(shí)完慧,就會(huì)導(dǎo)致你的app進(jìn)程被殺死谋旦!,這時(shí)候你再回到app頁(yè)面,系統(tǒng)會(huì)重走Activity的生命周期方法重建該Activity屈尼,這里會(huì)導(dǎo)致的一個(gè)問(wèn)題就是Activity中一些字段由于進(jìn)程被殺死時(shí)得不到保存而導(dǎo)致?tīng)顟B(tài)錯(cuò)誤册着,嚴(yán)重的可能導(dǎo)致頁(yè)面恢復(fù)時(shí)出現(xiàn)app崩潰問(wèn)題。
這里可以參考微信的做法脾歧,當(dāng)在系統(tǒng)設(shè)置中關(guān)閉權(quán)限回到app時(shí)讓app進(jìn)行重啟操作甲捏,重啟的代碼如下:
private static void restart(Activity activity) {
final Intent intent = new Intent();
intent.setClassName(activity, sSplashPage);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
activity.startActivity(intent);
activity.overridePendingTransition(0, 0);
}
sSplashPage即為啟動(dòng)頁(yè)的全名,通過(guò)startActivity重走啟動(dòng)頁(yè)鞭执,并通過(guò)FLAG_ACTIVITY_CLEAR_TASK清除掉棧中其余的activity司顿,達(dá)到重啟app的目的
剩下的一個(gè)問(wèn)題就是如何判斷app需要重啟,顯然必須是權(quán)限在系統(tǒng)設(shè)置中被關(guān)閉這時(shí)回到app頁(yè)面才需要觸發(fā)重啟兄纺。進(jìn)程被殺死然后恢復(fù)Activity一個(gè)顯著的特點(diǎn)就是savedInstanceState不為null免猾,但是僅僅根據(jù)savedInstanceState不為null就判斷app需要重啟顯然是不靠譜的。app橫豎屏切換就是典型的應(yīng)用場(chǎng)景囤热,難道你要每次橫豎屏切換都重啟app?
關(guān)于系統(tǒng)權(quán)限關(guān)閉導(dǎo)致app進(jìn)程殺死获三,網(wǎng)上有一篇文章可以參考http://www.reibang.com/p/cb68ca511776旁蔼,文章底部留言有人指出判斷重啟的方案锨苏,
但是經(jīng)過(guò)自己實(shí)測(cè)發(fā)現(xiàn)該思路并不可行,如公司的測(cè)試三星手機(jī)棺聊,在系統(tǒng)中權(quán)限關(guān)閉后會(huì)直接觸發(fā)application的onCreate方法伞租,而華為手機(jī)的application的onCreate方法會(huì)在你點(diǎn)擊app時(shí)才會(huì)執(zhí)行。這種差異就注定了留言中的方案不可行限佩。
這里提供一下自己解決這個(gè)問(wèn)題的思路葵诈,尋找app每次正常啟動(dòng)必經(jīng)的activity,如果檢測(cè)到該activity就認(rèn)為app是正常啟動(dòng) 祟同,之后即使出現(xiàn)savedInstanceState不為null也不觸發(fā)重啟作喘,這種必經(jīng)的activity一般都為splashActivity,特殊一點(diǎn)的如通過(guò)通知欄進(jìn)入app或者通過(guò)deeplink形式進(jìn)入app晕城,可以這個(gè)必經(jīng)Activity會(huì)變成一個(gè)中間過(guò)渡的Activity泞坦,但是思路上是和SplashActivity是一樣的。
如果app進(jìn)程被殺死然后恢復(fù)app頁(yè)面這種情況會(huì)直接跳過(guò)該splashActivity砖顷,從而就可以觸發(fā)app的重啟邏輯了贰锁。
根據(jù)以上思路便可以得到如下代碼:
public class RestartWatcher {
private static String sSplashPage = null;
private static List<String> sWhiteList;
private static boolean sRestartDisable;
private static void restart(Activity activity) {
final Intent intent = new Intent();
intent.setClassName(activity, sSplashPage);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
activity.startActivity(intent);
activity.overridePendingTransition(0, 0);
}
static void watch(Activity activity, Bundle savedInstanceState) {
try {
if (sSplashPage == null) {
sSplashPage = getLauncherActivityName(activity.getApplication());
}
if (TextUtils.isEmpty(sSplashPage) || savedInstanceState == null) {
return;
}
String name = activity.getClass().getCanonicalName();
if (TextUtils.isEmpty(name)) {
return;
}
if (sWhiteList != null && sWhiteList.contains(name)) {
return;
}
//noinspection ConstantConditions
if (name.equalsIgnoreCase(sSplashPage)) {
sRestartDisable = true;
}
if (!sRestartDisable) {
restart(activity);
}
} catch (Exception e) {
e.printStackTrace();
LogUtil.e("", "restart fail");
}
}
private static String getLauncherActivityName(Application application) {
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.setPackage(application.getPackageName());
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
// 通過(guò)查詢,獲得所有ResolveInfo對(duì)象.
List<ResolveInfo> resolveInfos = application.getPackageManager()
.queryIntentActivities(mainIntent, 0);
if (resolveInfos != null && !resolveInfos.isEmpty()) {
return resolveInfos.get(0).activityInfo.name;
}
return "";
}
}
}
通過(guò)如下代碼進(jìn)行注冊(cè)
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
RestartWatcher.watch(activity, savedInstanceState);
}
...
}
因?yàn)閍pp重啟本質(zhì)上是和權(quán)限設(shè)置相關(guān)滤蝠,所以可以考慮將這部分的代碼挪到權(quán)限庫(kù)當(dāng)中豌熄。
總結(jié):
到此就將自己在改造app權(quán)限時(shí)遇到的三個(gè)問(wèn)題都闡述完畢了,除了第一個(gè)問(wèn)題可能和公司內(nèi)部權(quán)限庫(kù)實(shí)現(xiàn)相關(guān)外物咳,剩下的兩個(gè)問(wèn)題應(yīng)該是比較共性的問(wèn)題锣险。文章分享了自己在解決上述問(wèn)題時(shí)的思路,不一定是最合理的方案所森,但是可以給其他人一些參考囱持。