V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
roadna
V2EX  ›  Android

初学 Android,请教一个关于 AlertDialog app 库和 support.v7.app 库的问题

  •  
  •   roadna · 2016-07-12 21:17:38 +08:00 · 10370 次点击
    这是一个创建于 2815 天前的主题,其中的信息可能已经有所发展或是发生改变。

    参考的书籍是《第一行代码》,此书基于 4.x 版本,而我的环境是 AS 2.1.2 , API 23 ,测试手机是 MotoX 2013 , API 22 。在通过广播接收器实现强制用户下线这个功能的时候,出现了一个关于 AlertDialog 的问题。

    1. import android.app.AlertDialog 时: 功能正常,收到广播后弹出 AlertDialog 。

    2. import android.support.v7.app.AlertDialog 时(默认引用的库):

    在 API 22 的手机中运行,能够收到 Log.d(TAG,"Broadcast received")的日志,但无法生成 AlertDialog 并且程序已停止运行,显示错误为“ Unable to start receiver ...略...You need to use a Theme.AppCompat theme (or descendant) with this activity ”。项目中的 Activity 均继承与 AppCompatActivity ,且 style parent=“ Theme.AppCompat.Light.DarkActionBar ”。

    在 API 23 的虚拟器上运行时,无法收到"Broadcast received"日志,但程序不会停止运行。

    //build.gradle
    apply plugin: 'com.android.application'
    
    android {
        compileSdkVersion 23
        buildToolsVersion "23.0.3"
    
        defaultConfig {
            applicationId "com.roadna.broadcastbestpractice"
            minSdkVersion 19
            targetSdkVersion 23
            versionCode 1
            versionName "1.0"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }
    
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        testCompile 'junit:junit:4.12'
        compile 'com.android.support:appcompat-v7:23.4.0'
    }
    
    //ForceOfflineReceiver.java
    
    package com.roadna.broadcastbestpractice;
    
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.support.v7.app.AlertDialog; 
    //import android.app.AlertDialog;
    import android.util.Log;
    import android.view.WindowManager;
    
    public class ForceOfflineReceiver extends BroadcastReceiver {
        public static final String TAG = "ForceOfflineReceiver";
        @Override
        public void onReceive(final Context context, Intent intent){
            Log.d(TAG, "Broadcast received");
            AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
            dialogBuilder.setTitle("Warning");
            dialogBuilder.setMessage("You are forced to be offline. Please try to login again.");
            dialogBuilder.setCancelable(false);
            dialogBuilder.setPositiveButton("OK",
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which){
                            ActivityCollector.finishAll();
                            Intent intent = new Intent(context, LoginActivity.class);
                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            context.startActivity(intent);
                        }
                    });
            AlertDialog alertDialog = dialogBuilder.create();
            alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
            alertDialog.show();
        }
    }
    
    

    附上 StackOverflow 上一个相关的帖子。更换 android.app.AlertDialog 库的解决方法就是参照其中一个答案,但并未触到问题的本质。

    不胜感激~

    第 1 条附言  ·  2016-07-14 22:51:47 +08:00
    在 API 23 的虚拟器中的结果与在 API22 的手机里一样,我的失误~
    第 2 条附言  ·  2016-07-15 00:07:32 +08:00

    首先谢谢各位的解答!

    Solution:

    错误信息:You need to use a Theme.AppCompat theme

    修改Builder:AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context,R.style.AlertDialogTheme);

    style.xml:<style name="AlertDialogTheme" parent="Theme.AppCompat.Dialog.Alert"></style>

    • 传入的context并非来自Activity,故不包含AppCompat的style,手动指定style可解决。
    • 两个库的不同:android.app.AlertDialog是根据不同版本系统而决定对话框样式的,android.support.v7.app.AlertDialog可以给低版本的安卓带来AppCompat的样式,所以Builder()时需要指定AppCompat的具体样式Light、Dark还是regular的。

    若有错误,请指正~

    15 条回复    2016-07-15 11:41:29 +08:00
    Override
        1
    Override  
       2016-07-12 22:06:00 +08:00
    我仿佛又听到有人在背后 @我
    roadna
        2
    roadna  
    OP
       2016-07-12 22:56:07 +08:00 via Android
    @Override 洗耳恭听
    boxgu
        3
    boxgu  
       2016-07-12 23:18:14 +08:00 via Android
    onReceive 方法传进来的参数 context, 不能用了生成 AlertDialog.Builder
    roadna
        4
    roadna  
    OP
       2016-07-12 23:41:45 +08:00 via Android
    @boxgu 那为什么 import android.app.AlertDialog 时又可以呢?我查看了两个库里 Builder 函数的实现是一样的,默认 resolveDialogTheme(context,0), 0 是 theme 的值,会不会是这里存在两个库的差异呢~
    wanttofly
        5
    wanttofly  
       2016-07-13 09:46:06 +08:00
    也许 http://blog.csdn.net/worst_hacker/article/details/49867043 第 54 条对你有点帮助。
    kyze8439690
        6
    kyze8439690  
       2016-07-13 10:32:11 +08:00
    因为 style 实际数据的获取从 context 中来,你这个 onReceive 的 context 并没有 AppCompat 的 style
    roadna
        7
    roadna  
    OP
       2016-07-13 10:56:12 +08:00 via Android
    @wanttofly 谢谢!加深了对 context 的理解
    bjzhou1990
        8
    bjzhou1990  
       2016-07-13 14:39:42 +08:00
    或者换一种理解方式, AlertDialog 的显示要求你的 App 必须在前台显示,但是收到广播时你的 App 可能已经关掉了, AlertDialog 当然不能显示,所以即使你拿到了正确的 context ,在 onReceiver 里弹 dialog 也是错误的使用方式
    roadna
        9
    roadna  
    OP
       2016-07-13 15:54:21 +08:00 via Android
    @bjzhou1990 例程里 app 一直处于前台但还是出现了 Alertdialog 打不开的情况,可能是 context 的问题,如 @kyze8439690 所说没有 App Compat style 。广播接收器里弹对话框的确是不推荐的。
    roadna
        10
    roadna  
    OP
       2016-07-15 00:08:53 +08:00
    已 Append ,请指教~
    @kyze8439690
    @bjzhou1990
    @wanttofly
    @boxgu
    wanttofly
        11
    wanttofly  
       2016-07-15 09:16:29 +08:00
    6 楼其实已经说的很明白了,我贴的那个链接里也写了啊。。你的 Activity 继承 AppCompatActivity ,所以会包含 v7 包下的 theme ,但是 Application 或者其他的 Context 是不包含的 V7 包的 theme 的,你只要换一下 Context 就好了啊。。。两个库的不同不是你说的那个不同吧。。
    kyze8439690
        12
    kyze8439690  
       2016-07-15 09:30:49 +08:00 via Android
    @roadna 也可以试试 ContextThemeWrapper 看看有没有效果
    roadna
        13
    roadna  
    OP
       2016-07-15 11:21:08 +08:00
    @wanttofly
    BroadcastReciver - 它本身不是 context ,也没有 context 在它里面,但是每当一个新的广播到达的时候,框架都传递一个 context 对象到 onReceive()。这个 context 是一个 ReceiverRestrictedContext 实例。
    这是一个文章里写的,见(链接)[http://blog.csdn.net/race604/article/details/9331807]
    你的意思是换成 Activity 的 Context ?那样不是会依赖于 activity 处于前台么?
    看到比较常见的做法是广播接收器启动一个对话框样式的 activity 。
    roadna
        14
    roadna  
    OP
       2016-07-15 11:31:46 +08:00
    @wanttofly
    这是两个包的一个对比: http://www.jianshu.com/p/6caffdbcd5db
    roadna
        15
    roadna  
    OP
       2016-07-15 11:41:29 +08:00
    @kyze8439690 好的, thx
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3930 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 40ms · UTC 10:25 · PVG 18:25 · LAX 03:25 · JFK 06:25
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.