Repository: wildma/MqttAndroidClient Branch: master Commit: b9194b8562e6 Files: 26 Total size: 42.3 KB Directory structure: gitextract_7dorb0mh/ ├── .gitignore ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── wildma/ │ │ └── mqttandroidclient/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── wildma/ │ │ │ └── mqttandroidclient/ │ │ │ ├── MainActivity.java │ │ │ ├── MyApplication.java │ │ │ ├── MyMqttService.java │ │ │ └── permission/ │ │ │ ├── DialogHelper.java │ │ │ ├── PermissionConstants.java │ │ │ └── PermissionUtils.java │ │ └── res/ │ │ ├── layout/ │ │ │ └── activity_main.xml │ │ ├── values/ │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── values-v21/ │ │ └── styles.xml │ └── test/ │ └── java/ │ └── com/ │ └── wildma/ │ └── mqttandroidclient/ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.iml .gradle /local.properties .idea .DS_Store /build /captures .externalNativeBuild ================================================ FILE: README.md ================================================ # MqttAndroidClient Android消息推送MQTT 详细介绍请看文章:[Android消息推送MQTT实战](https://www.jianshu.com/p/73436a5cf855) ps:如果对你有帮助,点下star就是对我最大的认可。 ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 26 buildToolsVersion "26.0.0" defaultConfig { applicationId "com.wildma.mqttandroidclient" minSdkVersion 14 targetSdkVersion 26 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } repositories { maven { url "https://repo.eclipse.org/content/repositories/paho-snapshots/" //MQTT } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:26.+' compile 'com.android.support.constraint:constraint-layout:1.0.2' testCompile 'junit:junit:4.12' /*MQTT*/ compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0' compile 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1' } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in E:\Android_studio\SDK/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: app/src/androidTest/java/com/wildma/mqttandroidclient/ExampleInstrumentedTest.java ================================================ package com.wildma.mqttandroidclient; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumentation test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("com.wildma.mqttandroidclient", appContext.getPackageName()); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/com/wildma/mqttandroidclient/MainActivity.java ================================================ package com.wildma.mqttandroidclient; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import com.wildma.mqttandroidclient.permission.DialogHelper; import com.wildma.mqttandroidclient.permission.PermissionConstants; import com.wildma.mqttandroidclient.permission.PermissionUtils; import java.util.List; public class MainActivity extends AppCompatActivity { private String TAG = this.getClass().getSimpleName(); private Intent mIntent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); requestPermission(); } public void publish(View view) { //模拟闸机设备发送消息过来 MyMqttService.publish("tourist enter"); } /** * 申请权限 */ public void requestPermission() { PermissionUtils.permission(PermissionConstants.PHONE) .rationale(new PermissionUtils.OnRationaleListener() { @Override public void rationale(final ShouldRequest shouldRequest) { Log.d(TAG, "onDenied: 权限被拒绝后弹框提示"); DialogHelper.showRationaleDialog(shouldRequest, MainActivity.this); } }) .callback(new PermissionUtils.FullCallback() { @Override public void onGranted(List permissionsGranted) { mIntent = new Intent(MainActivity.this, MyMqttService.class); //开启服务 startService(mIntent); } @Override public void onDenied(List permissionsDeniedForever, List permissionsDenied) { Log.d(TAG, "onDenied: 权限被拒绝"); if (!permissionsDeniedForever.isEmpty()) { DialogHelper.showOpenAppSettingDialog(MainActivity.this); } } }) .request(); } @Override protected void onDestroy() { super.onDestroy(); //停止服务 stopService(mIntent); } } ================================================ FILE: app/src/main/java/com/wildma/mqttandroidclient/MyApplication.java ================================================ package com.wildma.mqttandroidclient; import android.app.Application; import android.content.Context; /** * Desc ${MyApplication} */ public class MyApplication extends Application { private static Context mContext; @Override public void onCreate() { super.onCreate(); mContext = this; } public static Context getContext() { return mContext; } } ================================================ FILE: app/src/main/java/com/wildma/mqttandroidclient/MyMqttService.java ================================================ package com.wildma.mqttandroidclient; import android.annotation.SuppressLint; import android.app.Service; import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.support.annotation.Nullable; import android.util.Log; import android.widget.Toast; import org.eclipse.paho.android.service.MqttAndroidClient; import org.eclipse.paho.client.mqttv3.IMqttActionListener; import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; import org.eclipse.paho.client.mqttv3.IMqttToken; import org.eclipse.paho.client.mqttv3.MqttCallback; import org.eclipse.paho.client.mqttv3.MqttConnectOptions; import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttMessage; /** * Author wildma * Github https://github.com/wildma * CreateDate 2018/11/08 * Desc ${MQTT服务} */ public class MyMqttService extends Service { public final String TAG = MyMqttService.class.getSimpleName(); private static MqttAndroidClient mqttAndroidClient; private MqttConnectOptions mMqttConnectOptions; public String HOST = "tcp://192.168.1.10:61613";//服务器地址(协议+地址+端口号) public String USERNAME = "admin";//用户名 public String PASSWORD = "password";//密码 public static String PUBLISH_TOPIC = "tourist_enter";//发布主题 public static String RESPONSE_TOPIC = "message_arrived";//响应主题 @SuppressLint("MissingPermission") public String CLIENTID = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? Build.getSerial() : Build.SERIAL;//客户端ID,一般以客户端唯一标识符表示,这里用设备序列号表示 @Override public int onStartCommand(Intent intent, int flags, int startId) { init(); return super.onStartCommand(intent, flags, startId); } @Nullable @Override public IBinder onBind(Intent intent) { return null; } /** * 发布 (模拟其他客户端发布消息) * * @param message 消息 */ public static void publish(String message) { String topic = PUBLISH_TOPIC; Integer qos = 2; Boolean retained = false; try { //参数分别为:主题、消息的字节数组、服务质量、是否在服务器保留断开连接后的最后一条消息 mqttAndroidClient.publish(topic, message.getBytes(), qos.intValue(), retained.booleanValue()); } catch (MqttException e) { e.printStackTrace(); } } /** * 响应 (收到其他客户端的消息后,响应给对方告知消息已到达或者消息有问题等) * * @param message 消息 */ public void response(String message) { String topic = RESPONSE_TOPIC; Integer qos = 2; Boolean retained = false; try { //参数分别为:主题、消息的字节数组、服务质量、是否在服务器保留断开连接后的最后一条消息 mqttAndroidClient.publish(topic, message.getBytes(), qos.intValue(), retained.booleanValue()); } catch (MqttException e) { e.printStackTrace(); } } /** * 初始化 */ private void init() { String serverURI = HOST; //服务器地址(协议+地址+端口号) mqttAndroidClient = new MqttAndroidClient(this, serverURI, CLIENTID); mqttAndroidClient.setCallback(mqttCallback); //设置监听订阅消息的回调 mMqttConnectOptions = new MqttConnectOptions(); mMqttConnectOptions.setCleanSession(true); //设置是否清除缓存 mMqttConnectOptions.setConnectionTimeout(10); //设置超时时间,单位:秒 mMqttConnectOptions.setKeepAliveInterval(20); //设置心跳包发送间隔,单位:秒 mMqttConnectOptions.setUserName(USERNAME); //设置用户名 mMqttConnectOptions.setPassword(PASSWORD.toCharArray()); //设置密码 // last will message boolean doConnect = true; String message = "{\"terminal_uid\":\"" + CLIENTID + "\"}"; String topic = PUBLISH_TOPIC; Integer qos = 2; Boolean retained = false; if ((!message.equals("")) || (!topic.equals(""))) { // 最后的遗嘱 try { mMqttConnectOptions.setWill(topic, message.getBytes(), qos.intValue(), retained.booleanValue()); } catch (Exception e) { Log.i(TAG, "Exception Occured", e); doConnect = false; iMqttActionListener.onFailure(null, e); } } if (doConnect) { doClientConnection(); } } /** * 连接MQTT服务器 */ private void doClientConnection() { if (!mqttAndroidClient.isConnected() && isConnectIsNomarl()) { try { mqttAndroidClient.connect(mMqttConnectOptions, null, iMqttActionListener); } catch (MqttException e) { e.printStackTrace(); } } } /** * 判断网络是否连接 */ private boolean isConnectIsNomarl() { ConnectivityManager connectivityManager = (ConnectivityManager) this.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo info = connectivityManager.getActiveNetworkInfo(); if (info != null && info.isAvailable()) { String name = info.getTypeName(); Log.i(TAG, "当前网络名称:" + name); return true; } else { Log.i(TAG, "没有可用网络"); /*没有可用网络的时候,延迟3秒再尝试重连*/ new Handler().postDelayed(new Runnable() { @Override public void run() { doClientConnection(); } }, 3000); return false; } } //MQTT是否连接成功的监听 private IMqttActionListener iMqttActionListener = new IMqttActionListener() { @Override public void onSuccess(IMqttToken arg0) { Log.i(TAG, "连接成功 "); try { mqttAndroidClient.subscribe(PUBLISH_TOPIC, 2);//订阅主题,参数:主题、服务质量 } catch (MqttException e) { e.printStackTrace(); } } @Override public void onFailure(IMqttToken arg0, Throwable arg1) { arg1.printStackTrace(); Log.i(TAG, "连接失败 "); doClientConnection();//连接失败,重连(可关闭服务器进行模拟) } }; //订阅主题的回调 private MqttCallback mqttCallback = new MqttCallback() { @Override public void messageArrived(String topic, MqttMessage message) throws Exception { Log.i(TAG, "收到消息: " + new String(message.getPayload())); //收到消息,这里弹出Toast表示。如果需要更新UI,可以使用广播或者EventBus进行发送 Toast.makeText(getApplicationContext(), "messageArrived: " + new String(message.getPayload()), Toast.LENGTH_LONG).show(); //收到其他客户端的消息后,响应给对方告知消息已到达或者消息有问题等 response("message arrived"); } @Override public void deliveryComplete(IMqttDeliveryToken arg0) { } @Override public void connectionLost(Throwable arg0) { Log.i(TAG, "连接断开 "); doClientConnection();//连接断开,重连 } }; @Override public void onDestroy() { try { mqttAndroidClient.disconnect(); //断开连接 } catch (MqttException e) { e.printStackTrace(); } super.onDestroy(); } } ================================================ FILE: app/src/main/java/com/wildma/mqttandroidclient/permission/DialogHelper.java ================================================ package com.wildma.mqttandroidclient.permission; import android.app.Activity; import android.content.DialogInterface; import android.support.v7.app.AlertDialog; import com.wildma.mqttandroidclient.R; /** * Desc ${DialogHelper} */ public class DialogHelper { public static void showRationaleDialog(final PermissionUtils.OnRationaleListener.ShouldRequest shouldRequest, Activity activity) { if (activity == null) return; new AlertDialog.Builder(activity, R.style.Theme_AppCompat_Light_Dialog_Alert) .setTitle(android.R.string.dialog_alert_title) .setMessage(R.string.permission_denied_hint) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { shouldRequest.again(true); } }) .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { shouldRequest.again(false); } }) .setCancelable(false) .create() .show(); } public static void showOpenAppSettingDialog(Activity activity) { if (activity == null) return; new AlertDialog.Builder(activity, R.style.Theme_AppCompat_Light_Dialog_Alert) .setTitle(android.R.string.dialog_alert_title) .setMessage(R.string.permission_denied_forever_hint) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { PermissionUtils.launchAppDetailsSettings(); } }) .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }) .setCancelable(false) .create() .show(); } } ================================================ FILE: app/src/main/java/com/wildma/mqttandroidclient/permission/PermissionConstants.java ================================================ package com.wildma.mqttandroidclient.permission; import android.Manifest; import android.Manifest.permission; import android.annotation.SuppressLint; import android.support.annotation.StringDef; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Desc ${权限相关常量} */ @SuppressLint("InlinedApi") public final class PermissionConstants { public static final String CALENDAR = Manifest.permission_group.CALENDAR; public static final String CAMERA = Manifest.permission_group.CAMERA; public static final String CONTACTS = Manifest.permission_group.CONTACTS; public static final String LOCATION = Manifest.permission_group.LOCATION; public static final String MICROPHONE = Manifest.permission_group.MICROPHONE; public static final String PHONE = Manifest.permission_group.PHONE; public static final String SENSORS = Manifest.permission_group.SENSORS; public static final String SMS = Manifest.permission_group.SMS; public static final String STORAGE = Manifest.permission_group.STORAGE; private static final String[] GROUP_CALENDAR = { permission.READ_CALENDAR, permission.WRITE_CALENDAR }; private static final String[] GROUP_CAMERA = { permission.CAMERA }; private static final String[] GROUP_CONTACTS = { permission.READ_CONTACTS, permission.WRITE_CONTACTS, permission.GET_ACCOUNTS }; private static final String[] GROUP_LOCATION = { permission.ACCESS_FINE_LOCATION, permission.ACCESS_COARSE_LOCATION }; private static final String[] GROUP_MICROPHONE = { permission.RECORD_AUDIO }; private static final String[] GROUP_PHONE = { permission.READ_PHONE_STATE, permission.READ_PHONE_NUMBERS, permission.CALL_PHONE, permission.ANSWER_PHONE_CALLS, permission.READ_CALL_LOG, permission.WRITE_CALL_LOG, permission.ADD_VOICEMAIL, permission.USE_SIP, permission.PROCESS_OUTGOING_CALLS }; private static final String[] GROUP_SENSORS = { permission.BODY_SENSORS }; private static final String[] GROUP_SMS = { permission.SEND_SMS, permission.RECEIVE_SMS, permission.READ_SMS, permission.RECEIVE_WAP_PUSH, permission.RECEIVE_MMS, }; private static final String[] GROUP_STORAGE = { permission.READ_EXTERNAL_STORAGE, permission.WRITE_EXTERNAL_STORAGE }; @StringDef({CALENDAR, CAMERA, CONTACTS, LOCATION, MICROPHONE, PHONE, SENSORS, SMS, STORAGE,}) @Retention(RetentionPolicy.SOURCE) public @interface Permission { } public static String[] getPermissions(@Permission final String permission) { switch (permission) { case CALENDAR: return GROUP_CALENDAR; case CAMERA: return GROUP_CAMERA; case CONTACTS: return GROUP_CONTACTS; case LOCATION: return GROUP_LOCATION; case MICROPHONE: return GROUP_MICROPHONE; case PHONE: return GROUP_PHONE; case SENSORS: return GROUP_SENSORS; case SMS: return GROUP_SMS; case STORAGE: return GROUP_STORAGE; } return new String[]{permission}; } } ================================================ FILE: app/src/main/java/com/wildma/mqttandroidclient/permission/PermissionUtils.java ================================================ package com.wildma.mqttandroidclient.permission; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; import android.support.v4.content.ContextCompat; import android.util.Log; import android.view.MotionEvent; import android.view.WindowManager; import com.wildma.mqttandroidclient.MyApplication; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; /** * Desc ${权限工具类} * 注意:需要在清单文件注册PermissionActivity */ public final class PermissionUtils { private static final List PERMISSIONS = getPermissions(); private static PermissionUtils sInstance; private OnRationaleListener mOnRationaleListener; private SimpleCallback mSimpleCallback; private FullCallback mFullCallback; private ThemeCallback mThemeCallback; private Set mPermissions; private List mPermissionsRequest; private List mPermissionsGranted; private List mPermissionsDenied; private List mPermissionsDeniedForever; /** * 获取应用权限 * * @return 清单文件中的权限列表 */ public static List getPermissions() { return getPermissions(MyApplication.getContext().getPackageName()); } /** * 获取应用权限 * * @param packageName 包名 * @return 清单文件中的权限列表 */ public static List getPermissions(final String packageName) { PackageManager pm = MyApplication.getContext().getPackageManager(); try { return Arrays.asList( pm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS) .requestedPermissions ); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); return Collections.emptyList(); } } /** * 判断权限是否已授权 * * @param permissions * @return */ public static boolean isGranted(final String... permissions) { for (String permission : permissions) { if (!isGranted(permission)) { return false; } } return true; } private static boolean isGranted(final String permission) { return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(MyApplication.getContext(), permission); } /** * 打开应用详情设置界面 */ public static void launchAppDetailsSettings() { Intent intent = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS"); intent.setData(Uri.parse("package:" + MyApplication.getContext().getPackageName())); MyApplication.getContext().startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); } /** * 设置请求权限 * * @param permissions 要请求的权限 * @return {@link PermissionUtils} */ public static PermissionUtils permission(@PermissionConstants.Permission final String... permissions) { return new PermissionUtils(permissions); } private PermissionUtils(final String... permissions) { mPermissions = new LinkedHashSet<>(); for (String permission : permissions) { for (String aPermission : PermissionConstants.getPermissions(permission)) { if (PERMISSIONS.contains(aPermission)) { mPermissions.add(aPermission); } } } sInstance = this; } /** * Set rationale listener. * * @param listener The rationale listener. * @return the single {@link PermissionUtils} instance */ public PermissionUtils rationale(final OnRationaleListener listener) { mOnRationaleListener = listener; return this; } /** * Set the simple call back. * * @param callback the simple call back * @return the single {@link PermissionUtils} instance */ public PermissionUtils callback(final SimpleCallback callback) { mSimpleCallback = callback; return this; } /** * Set the full call back. * * @param callback the full call back * @return the single {@link PermissionUtils} instance */ public PermissionUtils callback(final FullCallback callback) { mFullCallback = callback; return this; } /** * Set the theme callback. * * @param callback The theme callback. * @return the single {@link PermissionUtils} instance */ public PermissionUtils theme(final ThemeCallback callback) { mThemeCallback = callback; return this; } /** * 开始请求 */ public void request() { mPermissionsGranted = new ArrayList<>(); mPermissionsRequest = new ArrayList<>(); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { mPermissionsGranted.addAll(mPermissions); requestCallback(); } else { for (String permission : mPermissions) { if (isGranted(permission)) { mPermissionsGranted.add(permission); } else { mPermissionsRequest.add(permission); } } if (mPermissionsRequest.isEmpty()) { requestCallback(); } else { startPermissionActivity(); } } } @RequiresApi(api = Build.VERSION_CODES.M) private void startPermissionActivity() { mPermissionsDenied = new ArrayList<>(); mPermissionsDeniedForever = new ArrayList<>(); PermissionActivity.start(MyApplication.getContext()); } @RequiresApi(api = Build.VERSION_CODES.M) private boolean rationale(final Activity activity) { boolean isRationale = false; if (mOnRationaleListener != null) { for (String permission : mPermissionsRequest) { if (activity.shouldShowRequestPermissionRationale(permission)) { getPermissionsStatus(activity); mOnRationaleListener.rationale(new OnRationaleListener.ShouldRequest() { @Override public void again(boolean again) { if (again) { startPermissionActivity(); } else { requestCallback(); } } }); isRationale = true; break; } } mOnRationaleListener = null; } return isRationale; } @RequiresApi(api = Build.VERSION_CODES.M) private void getPermissionsStatus(final Activity activity) { for (String permission : mPermissionsRequest) { if (isGranted(permission)) { mPermissionsGranted.add(permission); } else { mPermissionsDenied.add(permission); if (!activity.shouldShowRequestPermissionRationale(permission)) { mPermissionsDeniedForever.add(permission); } } } } private void requestCallback() { if (mSimpleCallback != null) { if (mPermissionsRequest.size() == 0 || mPermissions.size() == mPermissionsGranted.size()) { mSimpleCallback.onGranted(); } else { if (!mPermissionsDenied.isEmpty()) { mSimpleCallback.onDenied(); } } mSimpleCallback = null; } if (mFullCallback != null) { if (mPermissionsRequest.size() == 0 || mPermissions.size() == mPermissionsGranted.size()) { mFullCallback.onGranted(mPermissionsGranted); } else { if (!mPermissionsDenied.isEmpty()) { mFullCallback.onDenied(mPermissionsDeniedForever, mPermissionsDenied); } } mFullCallback = null; } mOnRationaleListener = null; mThemeCallback = null; } @RequiresApi(api = Build.VERSION_CODES.M) private void onRequestPermissionsResult(final Activity activity) { getPermissionsStatus(activity); requestCallback(); } @RequiresApi(api = Build.VERSION_CODES.M) public static class PermissionActivity extends Activity { public static void start(final Context context) { Intent starter = new Intent(context, PermissionActivity.class); starter.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(starter); } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH); if (sInstance == null) { super.onCreate(savedInstanceState); Log.e("PermissionUtils", "request permissions failed"); finish(); return; } if (sInstance.mThemeCallback != null) { sInstance.mThemeCallback.onActivityCreate(this); } super.onCreate(savedInstanceState); if (sInstance.rationale(this)) { finish(); return; } if (sInstance.mPermissionsRequest != null) { int size = sInstance.mPermissionsRequest.size(); if (size <= 0) { finish(); return; } requestPermissions(sInstance.mPermissionsRequest.toArray(new String[size]), 1); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { sInstance.onRequestPermissionsResult(this); finish(); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { finish(); return true; } } /////////////////////////////////////////////////////////////////////////// // interface /////////////////////////////////////////////////////////////////////////// /*设置拒绝权限后再次请求的回调接口*/ public interface OnRationaleListener { void rationale(ShouldRequest shouldRequest); interface ShouldRequest { void again(boolean again); } } /*是否授权回调接口*/ public interface SimpleCallback { void onGranted(); void onDenied(); } /*是否授权回调接口*/ public interface FullCallback { void onGranted(List permissionsGranted); void onDenied(List permissionsDeniedForever, List permissionsDenied); } /*主题回调接口*/ public interface ThemeCallback { void onActivityCreate(Activity activity); } } ================================================ FILE: app/src/main/res/layout/activity_main.xml ================================================