为什么Android AppOps(隐私管理器)在用户更改其隐私设置时发送重复事件?

时间:2014-04-30 03:19:21

标签: android listener

当用户通过AppOps更改他/她的隐私设置时(例如,拒绝应用程序访问电话联系人),AppOpsManager会向听取用户更改内容的任何人发送(即包名称和操作(例如,阅读联系人))

所以我写了一个听众来这样做。但是,我们用户只进行了一次更改,我收到了太多重复事件(例如,用户决定拒绝愤怒的Bird访问他/她的位置的10个事件),然后应用程序崩溃。

这是我的代码,为每对包&注册列表器。操作:

public void startWatchingOperations(AppOpsManager appOps, List<AppOpsManager.PackageOps> opsforapps) {
    SharedPreferences myAppListnerPreferences = getSharedPreferences(APP_OPS_PREFERENCES, Activity.MODE_PRIVATE);
    for (AppOpsManager.PackageOps o:opsforapps) {
        List<OpEntry> opEntry = o.getOps();
        //if I already assigned a listener to this pari of package & operation, then skip 
        if (myAppListnerPreferences.getBoolean(o.getPackageName(), false)==false) {
            for (OpEntry entry:opEntry) {
                //for each pair of package & operation, assign a new listener 
                ChangePrivacySettingsListener opsListner = new ChangePrivacySettingsListener(getApplicationContext());      
                     appOps.startWatchingMode(entry.getOp(),o.getPackageName(),opsListner);
                 }
            myAppListnerPreferences.edit().putBoolean(o.getPackageName(), true).apply();
         }
    }
}

以下是听众的片段

public class ChangePrivacySettingsListener implements AppOpsManager.Callback {
    public void opChanged(int op, String packageName) {
        AppOpsManager appOps=   (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
        PackageManager pkg = context.getPackageManager();
        try {
                            //this is an object to store the event: package name, 
                            // the operation that has been changed, & time stamp
            PrivacySetting privacySetting = new PrivacySetting();
            privacySetting.setPackageName(packageName);
            privacySetting.setOperation(OPERATIONS_STRINGS[op]);
            privacySetting.setDecisionTime(Calendar.getInstance(TimeZone.getDefault()).getTimeInMillis());
            privacySetting.setUserId(userId);
        } catch (NameNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
     }

她是AppOpsManager.java的一部分,可以让我听取用户的更改。

public class AppOpsManager {
    final HashMap<Callback, IAppOpsCallback> mModeWatchers
        = new HashMap<Callback, IAppOpsCallback>();

    public void startWatchingMode(int op, String packageName, final Callback callback) {
        synchronized (mModeWatchers) {
          IAppOpsCallback cb = mModeWatchers.get(callback);
          if (cb == null) {
              cb = new IAppOpsCallback.Stub() {
                  public void opChanged(int op, String packageName) {
                      callback.opChanged(op, packageName);
                  }
              };
              mModeWatchers.put(callback, cb);
          }
          try {
              mService.startWatchingMode(op, packageName, cb);
          } catch (RemoteException e) {
          }
   }
}

我仔细检查以确保我从未为每对包装分配过多个监听器&amp;操作。

我很欣赏有关潜在原因的提示。

Here is a link to AppOpsManager.java

2 个答案:

答案 0 :(得分:0)

尝试将ChangePrivacySettingsListener opsListner的减速度移到for块的外边:

public void startWatchingOperations(AppOpsManager appOps, List<AppOpsManager.PackageOps> opsforapps) {
ChangePrivacySettingsListener opsListner;
SharedPreferences myAppListnerPreferences = getSharedPreferences(APP_OPS_PREFERENCES, Activity.MODE_PRIVATE);
for (AppOpsManager.PackageOps o:opsforapps) {
    List<OpEntry> opEntry = o.getOps();
    //if I already assigned a listener to this pari of package & operation, then skip 
    if (myAppListnerPreferences.getBoolean(o.getPackageName(), false)==false) {
        for (OpEntry entry:opEntry) {
            //for each pair of package & operation, assign a new listener 
             opsListner = new ChangePrivacySettingsListener(getApplicationContext());      
                 appOps.startWatchingMode(entry.getOp(),o.getPackageName(),opsListner);
             }
        myAppListnerPreferences.edit().putBoolean(o.getPackageName(), true).apply();
     }
}

}

请告诉我发生了什么?

答案 1 :(得分:0)

如果这对某人有帮助,至少是Android Oreo,调用AppOpsManager.startWatchingMode(op, packageName, callback)会在callback的设置更改(1)时调用op对于任何包, AND (2),任何AppOps设置都会更改packageName。这可以从AppOpsService.java来源看到,特别是AppOpsService.startWatchingMode()注册回调,AppOpsService.setMode()在AppOps设置更改时调用回调。

例如,如果您使用startWatchingMode(appOps1, package1, callback)startWatchingMode(appOps2, package1, callback)注册回调, 当appOps3 package1的设置发生变化时,由于您已注册package1两次,因此将回调两次回调。如果appOps1的{​​{1}}发生了变化,则回调将被调用3次,因为您已为package1注册了一次,而appOps1则注册了两次。

解决方案是注册您感兴趣的AppOps(没有重复),package1参数设置为packageName,或者注册您感兴趣的一组包,null参数设置为op

此外,您需要使用AppOpsManager.OP_NONE确保所有侦听器都未注册(例如,在您的onDestroy活动中)。否则,回调条目将在活动生命周期中累积(直到应用程序终止),您将开始获取重复项。这也意味着您应该保留对所有已创建的侦听器的引用。