我已经添加了对清单的权限,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.appbubble">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
当我进入应用程序设置时,我没有看到任何许可,这很奇怪吗?
但是一旦打开应用程序,我就会收到此崩溃消息。
08-28 00:59:53.945 31761-31761/app.appbubble E/AndroidRuntime: FATAL EXCEPTION: main
Process: app.appbubble, PID: 31761
android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@2d54966 -- permission denied for this window type
at android.view.ViewRootImpl.setView(ViewRootImpl.java:875)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:337)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
at com.txusballesteros.bubbles.BubblesService$2.run(BubblesService.java:120)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7229)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
这是MainActivity:
package app.appbubble;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Toast;
import com.txusballesteros.bubbles.BubbleLayout;
import com.txusballesteros.bubbles.BubblesManager;
import com.txusballesteros.bubbles.OnInitializedCallback;
public class MainActivity extends AppCompatActivity {
private BubblesManager bubblesManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.SYSTEM_ALERT_WINDOW) != PackageManager.PERMISSION_GRANTED ){//Can add more as per requirement
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.SYSTEM_ALERT_WINDOW},
123);
}
}
setContentView(R.layout.activity_main);
// configure bublle manager
initializeBubbleManager();
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
addNewNotification();
}
});
}
/*
* Inflate notifation layout into bubble layout
*/
private void addNewNotification() {
BubbleLayout bubbleView = (BubbleLayout) LayoutInflater.from(MainActivity.this)
.inflate(R.layout.notification_layout, null);
// this method call when user remove notification layout
bubbleView.setOnBubbleRemoveListener(new BubbleLayout.OnBubbleRemoveListener() {
@Override
public void onBubbleRemoved(BubbleLayout bubble) {
Toast.makeText(getApplicationContext(), "Bubble removed !",
Toast.LENGTH_SHORT).show();
}
});
// this methoid call when cuser click on the notification layout( bubble layout)
bubbleView.setOnBubbleClickListener(new BubbleLayout.OnBubbleClickListener() {
@Override
public void onBubbleClick(BubbleLayout bubble) {
Toast.makeText(getApplicationContext(), "Clicked !",
Toast.LENGTH_SHORT).show();
}
});
// add bubble view into bubble manager
bubblesManager.addBubble(bubbleView, 60, 20);
}
/**
* Configure the trash layout with your BubblesManager builder.
*/
private void initializeBubbleManager() {
bubblesManager = new BubblesManager.Builder(this)
.setTrashLayout(R.layout.notification_trash_layout)
.build();
bubblesManager.initialize();
}
@Override
protected void onDestroy() {
super.onDestroy();
bubblesManager.recycle();
}
}
答案 0 :(得分:2)
我会在API 23上运行一个疯狂的猜测(由于缺少代码)并且从未请求过许可:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.SYSTEM_ALERT_WINDOW) != PackageManager.PERMISSION_GRANTED ){//Can add more as per requirement
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.SYSTEM_ALERT_WINDOW},
123);
}
}
对于开发和发布,最佳做法是signature protection level(in release)
&#34;签名保护等级&#34;意思?
仅当请求的应用程序使用与声明该权限的应用程序相同的证书进行签名时,系统才授予的权限。如果证书匹配,系统会自动授予权限,而不会通知用户或要求用户明确批准。
正如文档中所述:
注意:如果应用面向API级别23或更高级别,则应用用户必须通过权限管理屏幕向应用明确授予此权限。该应用通过发送动作ACTION_MANAGE_OVERLAY_PERMISSION来请求用户的批准。该应用可以通过调用Settings.canDrawOverlays()来检查它是否具有此授权。
请求的最佳做法;
这是一个相当有争议的话题,因为谷歌建议在使用它们时提出要求但我在使用之前已经看过Google应用程序请求。许多开发人员也对它有意义。
我的工作是在应用启动时请求。这意味着首先发生的事情是请求权限。
虽然您可以在单独的屏幕上请求向用户说明您需要权限的原因。
或者Google推荐,在您需要时提出请求。
编辑2:
public final static int REQUEST_CODE = 6341;
public void checkDrawOverlayPermission() {
/** check if we already have permission to draw over other apps */
if (!Settings.canDrawOverlays(Context)) {
/** if not construct intent to request permission */
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
/** request permission via start activity for result */
startActivityForResult(intent, REQUEST_CODE);
}
}
为什么这样?
有一些权限不像普通和危险权限那样行事。 SYSTEM_ALERT_WINDOW和WRITE_SETTINGS特别敏感,因此大多数应用程序不应使用它们。如果应用程序需要其中一个权限,则必须在清单中声明权限,并发送请求用户授权的意图。系统通过向用户显示详细的管理屏幕来响应意图。
忽略上面的代码(请求权限代码),这是有效的编辑2代码。