Android消息推送:手把手教你集成小米推送

前言

在Android開發(fā)中,消息推送功能的使用非常常見翁狐。

推送消息截圖

為了降低開發(fā)成本类溢,使用第三方推送是現(xiàn)今較為流行的解決方案。

今天,我將手把手教大家如何在你的應(yīng)用里集成小米推送

該文檔基于小米推送官方Demo進(jìn)行解析闯冷,并給出簡易推送Demo

看該文檔前砂心,請先閱讀我寫的另外兩篇文章:

史上最全解析Android消息推送解決方案

Android推送:第三方消息推送平臺詳細(xì)解析

目錄

目錄

1. 官方Demo解析

首先,我們先對小米官方的推送Demo進(jìn)行解析蛇耀。

請先到官網(wǎng)下載官方DemoSDK說明文檔

1.1 Demo概況

need-to-insert-img

Demo目錄

目錄說明:

DemoApplication類

繼承自Application類辩诞,其作用主要是:設(shè)置App的ID & Key、注冊推送服務(wù)

DemoMessageReceiver類

繼承自BroadcastReceiver纺涤,用于接收推送消息并對這些消息進(jìn)行處理

MainActivity

實(shí)現(xiàn)界面按鈕處理 & 設(shè)置本地推送方案

TimeIntervalDialog

設(shè)置推送的時間間段

接下來译暂,我將對每個類進(jìn)行詳細(xì)分析

1.2 詳細(xì)分析

1.2.1 DemoApplication類

繼承自Application類,其作用主要是:

設(shè)置App的ID & Key

注冊推送服務(wù)

接下來我們通過代碼來看下這兩個功能如何實(shí)現(xiàn):

DemoApplication.java

packagecom.xiaomi.mipushdemo;importandroid.app.ActivityManager;importandroid.app.ActivityManager.RunningAppProcessInfo;importandroid.app.Application;importandroid.content.Context;importandroid.os.Handler;importandroid.os.Message;importandroid.os.Process;importandroid.text.TextUtils;importandroid.util.Log;importandroid.widget.Toast;importcom.xiaomi.channel.commonutils.logger.LoggerInterface;importcom.xiaomi.mipush.sdk.Logger;importcom.xiaomi.mipush.sdk.MiPushClient;importjava.util.List;publicclassDemoApplicationextendsApplication{// 使用自己APP的ID(官網(wǎng)注冊的)privatestaticfinalString APP_ID ="1000270";// 使用自己APP的KEY(官網(wǎng)注冊的)privatestaticfinalString APP_KEY ="670100056270";// 此TAG在adb logcat中檢索自己所需要的信息撩炊, 只需在命令行終端輸入 adb logcat | grep// com.xiaomi.mipushdemopublicstaticfinalString TAG ="com.xiaomi.mipushdemo";privatestaticDemoHandler sHandler =null;privatestaticMainActivity sMainActivity =null;//為了提高推送服務(wù)的注冊率外永,官方Demo建議在Application的onCreate中初始化推送服務(wù)//你也可以根據(jù)需要,在其他地方初始化推送服務(wù)@OverridepublicvoidonCreate(){super.onCreate();//判斷用戶是否已經(jīng)打開App拧咳,詳細(xì)見下面方法定義if(shouldInit()) {//注冊推送服務(wù)//注冊成功后會向DemoMessageReceiver發(fā)送廣播// 可以從DemoMessageReceiver的onCommandResult方法中MiPushCommandMessage對象參數(shù)中獲取注冊信息MiPushClient.registerPush(this, APP_ID, APP_KEY);//參數(shù)說明//context:Android平臺上app的上下文伯顶,建議傳入當(dāng)前app的application context//appID:在開發(fā)者網(wǎng)站上注冊時生成的,MiPush推送服務(wù)頒發(fā)給app的唯一認(rèn)證標(biāo)識//appKey:在開發(fā)者網(wǎng)站上注冊時生成的,與appID相對應(yīng),用于驗(yàn)證appID是否合法}//下面是與測試相關(guān)的日志設(shè)置LoggerInterface newLogger =newLoggerInterface() {@OverridepublicvoidsetTag(String tag){// ignore}@Overridepublicvoidlog(String content, Throwable t){? ? ? ? ? ? ? ? Log.d(TAG, content, t);? ? ? ? ? ? }@Overridepublicvoidlog(String content){? ? ? ? ? ? ? ? Log.d(TAG, content);? ? ? ? ? ? }? ? ? ? };? ? ? ? Logger.setLogger(this, newLogger);if(sHandler ==null) {? ? ? ? ? ? sHandler =newDemoHandler(getApplicationContext());? ? ? ? }? ? }//通過判斷手機(jī)里的所有進(jìn)程是否有這個App的進(jìn)程//從而判斷該App是否有打開privatebooleanshouldInit(){//通過ActivityManager我們可以獲得系統(tǒng)里正在運(yùn)行的activities//包括進(jìn)程(Process)等壹瘟、應(yīng)用程序/包、服務(wù)(Service)汪厨、任務(wù)(Task)信息。ActivityManager am = ((ActivityManager) getSystemService(Context.ACTIVITY_SERVICE));? ? ? ? List processInfos = am.getRunningAppProcesses();? ? ? ? String mainProcessName = getPackageName();//獲取本App的唯一標(biāo)識intmyPid = Process.myPid();//利用一個增強(qiáng)for循環(huán)取出手機(jī)里的所有進(jìn)程for(RunningAppProcessInfo info : processInfos) {//通過比較進(jìn)程的唯一標(biāo)識和包名判斷進(jìn)程里是否存在該Appif(info.pid == myPid && mainProcessName.equals(info.processName)) {returntrue;? ? ? ? ? ? }? ? ? ? }returnfalse;? ? }publicstaticDemoHandlergetHandler(){returnsHandler;? ? }publicstaticvoidsetMainActivity(MainActivity activity){? ? ? ? sMainActivity = activity;? ? }//通過設(shè)置Handler來設(shè)置提示文案publicstaticclassDemoHandlerextendsHandler{privateContext context;publicDemoHandler(Context context){this.context = context;? ? ? ? }@OverridepublicvoidhandleMessage(Message msg){? ? ? ? ? ? String s = (String) msg.obj;if(sMainActivity !=null) {? ? ? ? ? ? ? ? sMainActivity.refreshLogInfo();? ? ? ? ? ? }if(!TextUtils.isEmpty(s)) {? ? ? ? ? ? ? ? Toast.makeText(context, s, Toast.LENGTH_LONG).show();? ? ? ? ? ? }? ? ? ? }? ? }}

總結(jié):

步驟1:先判斷應(yīng)用App是否已開啟 - 通過判斷系統(tǒng)里的進(jìn)程

通過靜態(tài)方法

publicstaticvoidregisterPush(Context context,StringappID,StringappKey)

進(jìn)行推送服務(wù)注冊愉择,詳細(xì)參數(shù)如下:

need-to-insert-img

為了提高注冊率,最好在Application的onCreate中初始化推送服務(wù)

你也可以根據(jù)需要织中,在其他地方初始化推送服務(wù)

1.2.2 DemoMessageReceiver類

繼承自PushMessageReceiver(抽象類锥涕,繼承自BroadcastReceiver),其作用主要是:

接收推送消息

對推送消息進(jìn)行處理

DemoMessageReceiver.java

package com.xiaomi.mipushdemo;import android.annotation.SuppressLint;import android.content.Context;import android.os.Message;import android.text.TextUtils;import android.util.Log;import com.xiaomi.mipush.sdk.ErrorCode;import com.xiaomi.mipush.sdk.MiPushClient;import com.xiaomi.mipush.sdk.MiPushCommandMessage;import com.xiaomi.mipush.sdk.MiPushMessage;import com.xiaomi.mipush.sdk.PushMessageReceiver;import java.text.SimpleDateFormat;import java.util.Date;import java.util.List;/** * 1狭吼、PushMessageReceiver 是個抽象類层坠,該類繼承了 BroadcastReceiver。 * 2刁笙、需要將自定義的 DemoMessageReceiver 注冊在 AndroidManifest.xmlpublic class DemoMessageReceiver extends PushMessageReceiver {? ? private String mRegId;? ? private String mTopic;? ? private String mAlias;? ? private String mAccount;? ? private String mStartTime;? ? private String mEndTime;? ? //透傳消息到達(dá)客戶端時調(diào)用? ? //作用:可通過參數(shù)message從而獲得透傳消息破花,具體請看官方SDK文檔? ? @Override? ? public void onReceivePassThroughMessage(Context context, MiPushMessage message) {? ? ? ? Log.v(DemoApplication.TAG,"onReceivePassThroughMessage is called. "+ message.toString());? ? ? ? Stringlog= context.getString(R.string.recv_passthrough_message, message.getContent());? ? ? ? MainActivity.logList.add(0, getSimpleDate() +" "+log);if(!TextUtils.isEmpty(message.getTopic())) {? ? ? ? ? ? mTopic = message.getTopic();? ? ? ? }elseif(!TextUtils.isEmpty(message.getAlias())) {? ? ? ? ? ? mAlias = message.getAlias();? ? ? ? }? ? ? ? Message msg = Message.obtain();? ? ? ? msg.obj =log;? ? ? ? DemoApplication.getHandler().sendMessage(msg);? ? }//通知消息到達(dá)客戶端時調(diào)用? ? //注:應(yīng)用在前臺時不彈出通知的通知消息到達(dá)客戶端時也會回調(diào)函數(shù)? ? //作用:通過參數(shù)message從而獲得通知消息,具體請看官方SDK文檔? ? ? @Override? ? public void onNotificationMessageArrived(Context context, MiPushMessage message) {? ? ? ? Log.v(DemoApplication.TAG,"onNotificationMessageArrived is called. "+ message.toString());? ? ? ? Stringlog= context.getString(R.string.arrive_notification_message, message.getContent());? ? ? ? MainActivity.logList.add(0, getSimpleDate() +" "+log);if(!TextUtils.isEmpty(message.getTopic())) {? ? ? ? ? ? mTopic = message.getTopic();? ? ? ? }elseif(!TextUtils.isEmpty(message.getAlias())) {? ? ? ? ? ? mAlias = message.getAlias();? ? ? ? }? ? ? ? Message msg = Message.obtain();? ? ? ? msg.obj =log;? ? ? ? DemoApplication.getHandler().sendMessage(msg);? ? }? ? ? ? //用戶手動點(diǎn)擊通知欄消息時調(diào)用? ? //注:應(yīng)用在前臺時不彈出通知的通知消息到達(dá)客戶端時也會回調(diào)函數(shù)? ? //作用:1. 通過參數(shù)message從而獲得通知消息疲吸,具體請看官方SDK文檔? ? //2. 設(shè)置用戶點(diǎn)擊消息后打開應(yīng)用 or 網(wǎng)頁 or 其他頁面? ? @Override? ? public void onNotificationMessageClicked(Context context, MiPushMessage message) {? ? ? ? Log.v(DemoApplication.TAG,"onNotificationMessageClicked is called. "+ message.toString());? ? ? ? Stringlog= context.getString(R.string.click_notification_message, message.getContent());? ? ? ? MainActivity.logList.add(0, getSimpleDate() +" "+log);if(!TextUtils.isEmpty(message.getTopic())) {? ? ? ? ? ? mTopic = message.getTopic();? ? ? ? }elseif(!TextUtils.isEmpty(message.getAlias())) {? ? ? ? ? ? mAlias = message.getAlias();? ? ? ? }? ? ? ? Message msg = Message.obtain();if(message.isNotified()) {? ? ? ? ? ? msg.obj =log;? ? ? ? }? ? ? ? DemoApplication.getHandler().sendMessage(msg);? ? }? ? ? ? //用來接收客戶端向服務(wù)器發(fā)送命令后的響應(yīng)結(jié)果座每。? ? @Override? ? public void onCommandResult(Context context, MiPushCommandMessage message) {? ? ? ? Log.v(DemoApplication.TAG,"onCommandResult is called. "+ message.toString());? ? ? ? Stringcommand= message.getCommand();? ? ? ? List arguments = message.getCommandArguments();? ? ? ? String cmdArg1 = ((arguments != null && arguments.size() > 0) ? arguments.get(0) : null);? ? ? ? String cmdArg2 = ((arguments != null && arguments.size() > 1) ? arguments.get(1) : null);? ? ? ? Stringlog;if(MiPushClient.COMMAND_REGISTER.equals(command)) {if(message.getResultCode() == ErrorCode.SUCCESS) {? ? ? ? ? ? ? ? mRegId = cmdArg1;log= context.getString(R.string.register_success);? ? ? ? ? ? }else{log= context.getString(R.string.register_fail);? ? ? ? ? ? }? ? ? ? }elseif(MiPushClient.COMMAND_SET_ALIAS.equals(command)) {if(message.getResultCode() == ErrorCode.SUCCESS) {? ? ? ? ? ? ? ? mAlias = cmdArg1;log= context.getString(R.string.set_alias_success, mAlias);? ? ? ? ? ? }else{log= context.getString(R.string.set_alias_fail, message.getReason());? ? ? ? ? ? }? ? ? ? }elseif(MiPushClient.COMMAND_UNSET_ALIAS.equals(command)) {if(message.getResultCode() == ErrorCode.SUCCESS) {? ? ? ? ? ? ? ? mAlias = cmdArg1;log= context.getString(R.string.unset_alias_success, mAlias);? ? ? ? ? ? }else{log= context.getString(R.string.unset_alias_fail, message.getReason());? ? ? ? ? ? }? ? ? ? }elseif(MiPushClient.COMMAND_SET_ACCOUNT.equals(command)) {if(message.getResultCode() == ErrorCode.SUCCESS) {? ? ? ? ? ? ? ? mAccount = cmdArg1;log= context.getString(R.string.set_account_success, mAccount);? ? ? ? ? ? }else{log= context.getString(R.string.set_account_fail, message.getReason());? ? ? ? ? ? }? ? ? ? }elseif(MiPushClient.COMMAND_UNSET_ACCOUNT.equals(command)) {if(message.getResultCode() == ErrorCode.SUCCESS) {? ? ? ? ? ? ? ? mAccount = cmdArg1;log= context.getString(R.string.unset_account_success, mAccount);? ? ? ? ? ? }else{log= context.getString(R.string.unset_account_fail, message.getReason());? ? ? ? ? ? }? ? ? ? }elseif(MiPushClient.COMMAND_SUBSCRIBE_TOPIC.equals(command)) {if(message.getResultCode() == ErrorCode.SUCCESS) {? ? ? ? ? ? ? ? mTopic = cmdArg1;log= context.getString(R.string.subscribe_topic_success, mTopic);? ? ? ? ? ? }else{log= context.getString(R.string.subscribe_topic_fail, message.getReason());? ? ? ? ? ? }? ? ? ? }elseif(MiPushClient.COMMAND_UNSUBSCRIBE_TOPIC.equals(command)) {if(message.getResultCode() == ErrorCode.SUCCESS) {? ? ? ? ? ? ? ? mTopic = cmdArg1;log= context.getString(R.string.unsubscribe_topic_success, mTopic);? ? ? ? ? ? }else{log= context.getString(R.string.unsubscribe_topic_fail, message.getReason());? ? ? ? ? ? }? ? ? ? }elseif(MiPushClient.COMMAND_SET_ACCEPT_TIME.equals(command)) {if(message.getResultCode() == ErrorCode.SUCCESS) {? ? ? ? ? ? ? ? mStartTime = cmdArg1;? ? ? ? ? ? ? ? mEndTime = cmdArg2;log= context.getString(R.string.set_accept_time_success, mStartTime, mEndTime);? ? ? ? ? ? }else{log= context.getString(R.string.set_accept_time_fail, message.getReason());? ? ? ? ? ? }? ? ? ? }else{log= message.getReason();? ? ? ? }? ? ? ? MainActivity.logList.add(0, getSimpleDate() +"? ? "+log);? ? ? ? Message msg = Message.obtain();? ? ? ? msg.obj =log;? ? ? ? DemoApplication.getHandler().sendMessage(msg);? ? }? ? //用于接收客戶端向服務(wù)器發(fā)送注冊命令后的響應(yīng)結(jié)果。? ? @Override? ? public void onReceiveRegisterResult(Context context, MiPushCommandMessage message) {? ? ? ? Log.v(DemoApplication.TAG,"onReceiveRegisterResult is called. "+ message.toString());? ? ? ? Stringcommand= message.getCommand();? ? ? ? List arguments = message.getCommandArguments();? ? ? ? String cmdArg1 = ((arguments != null && arguments.size() > 0) ? arguments.get(0) : null);? ? ? ? Stringlog;if(MiPushClient.COMMAND_REGISTER.equals(command)) {if(message.getResultCode() == ErrorCode.SUCCESS) {? ? ? ? ? ? ? ? mRegId = cmdArg1;? ? ? ? ? ? ? ? //打印日志:注冊成功log= context.getString(R.string.register_success);? ? ? ? ? ? }else{? ? ? ? ? ? ? ? ? ? ? //打印日志:注冊失敗log= context.getString(R.string.register_fail);? ? ? ? ? ? }? ? ? ? }else{log= message.getReason();? ? ? ? }? ? ? ? Message msg = Message.obtain();? ? ? ? msg.obj =log;? ? ? ? DemoApplication.getHandler().sendMessage(msg);? ? }? ? @SuppressLint("SimpleDateFormat")? ? private static StringgetSimpleDate() {returnnew SimpleDateFormat("MM-dd hh:mm:ss").format(new Date());? ? }}

總結(jié)

根據(jù)需要復(fù)寫PushMessageReceiver里對消息的相關(guān)處理方法摘悴,以下是相關(guān)方法的詳情:

need-to-insert-img

相關(guān)方法詳情

關(guān)于onCommandResult(Context context峭梳,MiPushCommandMessage message)

a. 作用:當(dāng)客戶端向服務(wù)器發(fā)送注冊push、設(shè)置alias蹂喻、取消注冊alias葱椭、訂閱topic捂寿、取消訂閱topic等等命令后,從服務(wù)器返回結(jié)果孵运。

b. 參數(shù)說明

need-to-insert-img

參數(shù)說明

1.2.3 MainActivity

用于給用戶設(shè)置標(biāo)識秦陋,如別名、標(biāo)簽治笨、賬號等等

MainActivity.java

publicclassMainActivityextendsActivity{publicstaticList logList =newCopyOnWriteArrayList();privateTextView mLogView =null;@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);? ? ? ? setContentView(R.layout.activity_main);? ? ? ? DemoApplication.setMainActivity(this);? ? ? ? mLogView = (TextView) findViewById(R.id.log);// 設(shè)置別名findViewById(R.id.set_alias).setOnClickListener(newOnClickListener() {@OverridepublicvoidonClick(View v){finalEditText editText =newEditText(MainActivity.this);newAlertDialog.Builder(MainActivity.this)? ? ? ? ? ? ? ? ? ? ? ? .setTitle(R.string.set_alias)? ? ? ? ? ? ? ? ? ? ? ? .setView(editText)? ? ? ? ? ? ? ? ? ? ? ? .setPositiveButton(R.string.ok,newDialogInterface.OnClickListener() {@OverridepublicvoidonClick(DialogInterface dialog,intwhich){? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? String alias = editText.getText().toString();//調(diào)用靜態(tài)方法進(jìn)行設(shè)置? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? MiPushClient.setAlias(MainActivity.this, alias, null);}? ? ? ? ? ? ? ? ? ? ? ? })? ? ? ? ? ? ? ? ? ? ? ? .setNegativeButton(R.string.cancel,null)? ? ? ? ? ? ? ? ? ? ? ? .show();? ? ? ? ? ? }? ? ? ? });// 撤銷別名findViewById(R.id.unset_alias).setOnClickListener(newOnClickListener() {@OverridepublicvoidonClick(View v){finalEditText editText =newEditText(MainActivity.this);newAlertDialog.Builder(MainActivity.this)? ? ? ? ? ? ? ? ? ? ? ? .setTitle(R.string.unset_alias)? ? ? ? ? ? ? ? ? ? ? ? .setView(editText)? ? ? ? ? ? ? ? ? ? ? ? .setPositiveButton(R.string.ok,newDialogInterface.OnClickListener() {@OverridepublicvoidonClick(DialogInterface dialog,intwhich){? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? String alias = editText.getText().toString();//調(diào)用靜態(tài)方法進(jìn)行設(shè)置? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? MiPushClient.unsetAlias(MainActivity.this, alias, null);}? ? ? ? ? ? ? ? ? ? ? ? })? ? ? ? ? ? ? ? ? ? ? ? .setNegativeButton(R.string.cancel,null)? ? ? ? ? ? ? ? ? ? ? ? .show();? ? ? ? ? ? }? ? ? ? });// 設(shè)置帳號findViewById(R.id.set_account).setOnClickListener(newOnClickListener() {@OverridepublicvoidonClick(View v){finalEditText editText =newEditText(MainActivity.this);newAlertDialog.Builder(MainActivity.this)? ? ? ? ? ? ? ? ? ? ? ? .setTitle(R.string.set_account)? ? ? ? ? ? ? ? ? ? ? ? .setView(editText)? ? ? ? ? ? ? ? ? ? ? ? .setPositiveButton(R.string.ok,newDialogInterface.OnClickListener() {@OverridepublicvoidonClick(DialogInterface dialog,intwhich){? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? String account = editText.getText().toString();//調(diào)用靜態(tài)方法進(jìn)行設(shè)置? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? MiPushClient.setUserAccount(MainActivity.this, account, null);}? ? ? ? ? ? ? ? ? ? ? ? })? ? ? ? ? ? ? ? ? ? ? ? .setNegativeButton(R.string.cancel,null)? ? ? ? ? ? ? ? ? ? ? ? .show();? ? ? ? ? ? }? ? ? ? });// 撤銷帳號findViewById(R.id.unset_account).setOnClickListener(newOnClickListener() {@OverridepublicvoidonClick(View v){finalEditText editText =newEditText(MainActivity.this);newAlertDialog.Builder(MainActivity.this)? ? ? ? ? ? ? ? ? ? ? ? .setTitle(R.string.unset_account)? ? ? ? ? ? ? ? ? ? ? ? .setView(editText)? ? ? ? ? ? ? ? ? ? ? ? .setPositiveButton(R.string.ok,newDialogInterface.OnClickListener() {@OverridepublicvoidonClick(DialogInterface dialog,intwhich){? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? String account = editText.getText().toString();//調(diào)用靜態(tài)方法進(jìn)行設(shè)置? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? MiPushClient.unsetUserAccount(MainActivity.this, account, null);}? ? ? ? ? ? ? ? ? ? ? ? })? ? ? ? ? ? ? ? ? ? ? ? .setNegativeButton(R.string.cancel,null)? ? ? ? ? ? ? ? ? ? ? ? .show();? ? ? ? ? ? }? ? ? ? });// 設(shè)置標(biāo)簽findViewById(R.id.subscribe_topic).setOnClickListener(newOnClickListener() {@OverridepublicvoidonClick(View v){finalEditText editText =newEditText(MainActivity.this);newAlertDialog.Builder(MainActivity.this)? ? ? ? ? ? ? ? ? ? ? ? .setTitle(R.string.subscribe_topic)? ? ? ? ? ? ? ? ? ? ? ? .setView(editText)? ? ? ? ? ? ? ? ? ? ? ? .setPositiveButton(R.string.ok,newDialogInterface.OnClickListener() {@OverridepublicvoidonClick(DialogInterface dialog,intwhich){? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? String topic = editText.getText().toString();//調(diào)用靜態(tài)方法進(jìn)行設(shè)置? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? MiPushClient.subscribe(MainActivity.this, topic, null);}? ? ? ? ? ? ? ? ? ? ? ? })? ? ? ? ? ? ? ? ? ? ? ? .setNegativeButton(R.string.cancel,null)? ? ? ? ? ? ? ? ? ? ? ? .show();? ? ? ? ? ? }? ? ? ? });// 撤銷標(biāo)簽findViewById(R.id.unsubscribe_topic).setOnClickListener(newOnClickListener() {@OverridepublicvoidonClick(View v){finalEditText editText =newEditText(MainActivity.this);newAlertDialog.Builder(MainActivity.this)? ? ? ? ? ? ? ? ? ? ? ? .setTitle(R.string.unsubscribe_topic)? ? ? ? ? ? ? ? ? ? ? ? .setView(editText)? ? ? ? ? ? ? ? ? ? ? ? .setPositiveButton(R.string.ok,newDialogInterface.OnClickListener() {@OverridepublicvoidonClick(DialogInterface dialog,intwhich){? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? String topic = editText.getText().toString();//調(diào)用靜態(tài)方法進(jìn)行設(shè)置? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? MiPushClient.unsubscribe(MainActivity.this, topic, null);}? ? ? ? ? ? ? ? ? ? ? ? })? ? ? ? ? ? ? ? ? ? ? ? .setNegativeButton(R.string.cancel,null)? ? ? ? ? ? ? ? ? ? ? ? .show();? ? ? ? ? ? }? ? ? ? });// 設(shè)置接收消息時間findViewById(R.id.set_accept_time).setOnClickListener(newOnClickListener() {@OverridepublicvoidonClick(View v){newTimeIntervalDialog(MainActivity.this,newTimeIntervalInterface() {@Overridepublicvoidapply(intstartHour,intstartMin,intendHour,intendMin){//調(diào)用靜態(tài)方法進(jìn)行設(shè)置? MiPushClient.setAcceptTime(MainActivity.this, startHour, startMin, endHour, endMin,null);? ? ? ? ? ? ? ? ? ? }@Overridepublicvoidcancel(){//ignore}? ? ? ? ? ? ? ? })? ? ? ? ? ? ? ? ? ? ? ? .show();? ? ? ? ? ? }? ? ? ? });// 暫停推送findViewById(R.id.pause_push).setOnClickListener(newOnClickListener() {@OverridepublicvoidonClick(View v){? ? ? ? ? ? ? ? MiPushClient.pausePush(MainActivity.this,null);? ? ? ? ? ? }? ? ? ? });? ? ? ? findViewById(R.id.resume_push).setOnClickListener(newOnClickListener() {@OverridepublicvoidonClick(View v){//調(diào)用靜態(tài)方法進(jìn)行設(shè)置? MiPushClient.resumePush(MainActivity.this,null);? ? ? ? ? ? }? ? ? ? });? ? }@OverrideprotectedvoidonResume(){super.onResume();? ? ? ? refreshLogInfo();? ? }@OverrideprotectedvoidonDestroy(){super.onDestroy();? ? ? ? DemoApplication.setMainActivity(null);? ? }publicvoidrefreshLogInfo(){? ? ? ? String AllLog ="";for(String log : logList) {? ? ? ? ? ? AllLog = AllLog + log +"\n\n";? ? ? ? }? ? ? ? mLogView.setText(AllLog);? ? }}

總結(jié)

根據(jù)需求對不同用戶設(shè)置不同的推送標(biāo)識驳概,如別名、標(biāo)簽等等大磺。

a. 別名(Alias)

開發(fā)者可以為指定用戶設(shè)置別名抡句,然后給這個別名推送消息,

效果等同于給RegId推送消息杠愧,Alias是除Regid(自動生成的)和UserAccount之外的第三個用戶標(biāo)識

開發(fā)者可以取消指定用戶的某個別名待榔,服務(wù)器就不會給這個別名推送消息了。

//設(shè)置別名MiPushClient.setAlias(Context context,Stringalias,Stringcategory)流济;//撤銷別名MiPushClient.unsetAlias(Context context,Stringalias,Stringcategory)锐锣;//參數(shù)說明//context:Android平臺上app的上下文,建議傳入當(dāng)前app的application context//alias:為指定用戶設(shè)置別名 / 為指定用戶取消別名//category:擴(kuò)展參數(shù)绳瘟,暫時沒有用途雕憔,直接填null//獲取該客戶端所有的別名publicstaticList getAllAlias(final Context context)

b. 用戶賬號(UserAccoun)

開發(fā)者可以為指定用戶設(shè)置userAccount

開發(fā)者可以取消指定用戶的某個userAccount,服務(wù)器就不會給這個userAccount推送消息了

//設(shè)置MiPushClient.setUserAccount(finalContext context,finalString userAccount, Stringcategory)//撤銷MiPushClient.unsetUserAccount(finalContext context,finalString userAccount, Stringcategory)//參數(shù)說明//context:Android平臺上app的上下文糖声,建議傳入當(dāng)前app的application context//userAccount:為指定用戶設(shè)置userAccount / 為指定用戶取消userAccount//category:擴(kuò)展參數(shù)斤彼,暫時沒有用途,直接填null//獲取該客戶端所有設(shè)置的賬號publicstaticList getAllUserAccount(finalContext context)

c. 標(biāo)簽(Topic)

開發(fā)者可以結(jié)合自己的業(yè)務(wù)特征蘸泻,給用戶打上不同的標(biāo)簽琉苇。

消息推送時,開發(fā)者可以結(jié)合每條消息的內(nèi)容和目標(biāo)用戶悦施,為每條消息選擇對應(yīng)的標(biāo)簽并扇,為開發(fā)者可以根據(jù)訂閱的主題實(shí)現(xiàn)分組群發(fā),從而進(jìn)行消息的精準(zhǔn)推送

//設(shè)置標(biāo)簽MiPushClient.subscribe(Context context,Stringtopic,Stringcategory)抡诞;//撤銷標(biāo)簽MiPushClient.unsubscribe(Context context,Stringtopic,Stringcategory)穷蛹;//參數(shù)說明//context:Android平臺上app的上下文,建議傳入當(dāng)前app的application context//topic:為指定用戶設(shè)置設(shè)置訂閱的主題 / 為指定用戶取消訂閱的主題//category:擴(kuò)展參數(shù)昼汗,暫時沒有用途肴熏,直接填null//獲取該客戶端所有的標(biāo)簽publicstaticList getAllTopic(final Context context);

TimeIntervalDialog

作用:用于設(shè)置推送的時間-開始時間+暫停時間

packagecom.xiaomi.mipushdemo;importandroid.app.Dialog;importandroid.content.Context;importandroid.os.Bundle;importandroid.view.View;importandroid.widget.Button;importandroid.widget.TimePicker;importandroid.widget.TimePicker.OnTimeChangedListener;//繼承OnTimeChangedListener接口publicclassTimeIntervalDialogextendsDialogimplementsOnTimeChangedListener{privateTimeIntervalInterface mTimeIntervalInterface;privateContext mContext;privateTimePicker mStartTimePicker, mEndTimePicker;privateintmStartHour, mStartMinute, mEndHour, mEndMinute;privateButton.OnClickListener clickListener =newButton.OnClickListener() {@OverridepublicvoidonClick(View v){switch(v.getId()) {caseR.id.apply:? ? ? ? ? ? ? ? ? ? dismiss();//設(shè)置時間參數(shù)mTimeIntervalInterface.apply(mStartHour, mStartMinute, mEndHour, mEndMinute);break;caseR.id.cancel:? ? ? ? ? ? ? ? ? ? dismiss();? ? ? ? ? ? ? ? ? ? mTimeIntervalInterface.cancel();break;default:break;? ? ? ? ? ? }? ? ? ? }? ? };publicTimeIntervalDialog(Context context, TimeIntervalInterface timeIntervalInterface,intstartHour,intstartMinute,intendHour,intendMinute){super(context);? ? ? ? mContext = context;this.mTimeIntervalInterface = timeIntervalInterface;this.mStartHour = startHour;this.mStartMinute = startMinute;this.mEndHour = endHour;this.mEndMinute = endMinute;? ? }publicTimeIntervalDialog(Context context, TimeIntervalInterface timeIntervalInterface){this(context, timeIntervalInterface,0,0,23,59);? ? }@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);? ? ? ? setContentView(R.layout.set_time_dialog);? ? ? ? setCancelable(true);? ? ? ? setTitle(mContext.getString(R.string.set_accept_time));? ? ? ? mStartTimePicker = (TimePicker) findViewById(R.id.startTimePicker);? ? ? ? mStartTimePicker.setIs24HourView(true);? ? ? ? mStartTimePicker.setCurrentHour(mStartHour);? ? ? ? mStartTimePicker.setCurrentMinute(mStartMinute);? ? ? ? mStartTimePicker.setOnTimeChangedListener(this);? ? ? ? mEndTimePicker = (TimePicker) findViewById(R.id.endTimePicker);? ? ? ? mEndTimePicker.setIs24HourView(true);? ? ? ? mEndTimePicker.setCurrentHour(mEndHour);? ? ? ? mEndTimePicker.setCurrentMinute(mEndMinute);? ? ? ? mEndTimePicker.setOnTimeChangedListener(this);? ? ? ? Button applyBtn = (Button) findViewById(R.id.apply);? ? ? ? applyBtn.setOnClickListener(clickListener);? ? ? ? Button cancelBtn = (Button) findViewById(R.id.cancel);? ? ? ? cancelBtn.setOnClickListener(clickListener);? ? }@OverridepublicvoidonTimeChanged(TimePicker view,inthourOfDay,intminute){if(view == mStartTimePicker) {? ? ? ? ? ? mStartHour = hourOfDay;? ? ? ? ? ? mStartMinute = minute;? ? ? ? }elseif(view == mEndTimePicker) {? ? ? ? ? ? mEndHour = hourOfDay;? ? ? ? ? ? mEndMinute = minute;? ? ? ? }? ? }interfaceTimeIntervalInterface{voidapply(intstartHour,intstartMin,intendHour,intendMin);voidcancel();? ? }}

總結(jié)

使用一個繼承了Dialog類的TimeIntervalDialog類進(jìn)行推送時間的配置

可進(jìn)行的配置:設(shè)置推送時間(開始 & 結(jié)束)乔遮、暫停推送時間扮超、恢復(fù)推送時間

//設(shè)置推送時間(開始 & 結(jié)束)MiPushClient.setAcceptTime(Context context,intstartHour,intstartMin,intendHour,intendMin, String category)//設(shè)置暫停推送時間、恢復(fù)推送時間pausePush(Context context, String category)`和`resumePush(Context context, String category)//參數(shù)說明//context:Android平臺上app的上下文,建議傳入當(dāng)前app的application context//startHour:接收時段開始時間的小時//startMin? :接收時段開始時間的分鐘//endHour:接收時段結(jié)束時間的小時//endMin:接收時段結(jié)束時間的分鐘//category:擴(kuò)展參數(shù)出刷,暫時沒有用途璧疗,直接填null

AndroidManifest文件的配置

//小米推送支持最低的Android版本是2.2//設(shè)置一系列權(quán)限//這里com.xiaomi.mipushdemo改成自身app的包名//這里com.xiaomi.mipushdemo改成自身app的包名//注冊廣播BroadcastReceiver & Service//都是靜態(tài)注冊,因?yàn)橐L期處在后臺運(yùn)行//注:共是3個廣播接收器和4個服務(wù)馁龟,其中包括繼承了PushMessageReceiver的DemoMessageReceiver? ? ? ? ? ? ? ? ? ? ? ? //4個后臺服務(wù)//此service必須在3.0.1版本以后(包括3.0.1版本)加入//此service必須在2.2.5版本以后(包括2.2.5版本)加入//3個廣播//繼承了PushMessageReceiver的DemoMessageReceiver的廣播注冊

2. 集成小米推送步驟匯總

步驟1:在小米推送平臺進(jìn)行相關(guān)注冊開發(fā)者賬號崩侠,并進(jìn)行應(yīng)用的注冊:應(yīng)用包名,AppID和AppKey

步驟2:將小米推送的SDK包加入庫

步驟3:在應(yīng)用內(nèi)初始化小米推送服務(wù)

步驟4:繼承PushMessageReceiver坷檩,并復(fù)寫相關(guān)推送消息的方法

步驟5:在AndroidManifest文件里面配置好權(quán)限却音、注冊Service和BroadcastReceiver

在Android6.0里面的權(quán)限需要動態(tài)獲取

步驟6:根據(jù)需要設(shè)置一系列的推送設(shè)置,如用戶別名矢炼、標(biāo)簽等等

接下來系瓢,我們來按照上面的步驟,一步步來實(shí)現(xiàn)一個簡易的小米推送Demo

3. 實(shí)例解析

步驟1:在小米推送平臺進(jìn)行相關(guān)注冊開發(fā)者賬號句灌,并進(jìn)行應(yīng)用的注冊:應(yīng)用包名夷陋,AppID和AppKey

注意,填入的包名要跟你的應(yīng)用App的包名是一致的

創(chuàng)建應(yīng)用

AppID和Key

步驟2:將小米推送的SDK包加入到你應(yīng)用的庫里

放入到app/libs文件夾下胰锌,然后右鍵點(diǎn)擊add as Library骗绕,最后點(diǎn)擊Model就導(dǎo)入成功了

點(diǎn)擊此處進(jìn)行下載

小米推送SDK

導(dǎo)入包

步驟3:在應(yīng)用內(nèi)初始化小米推送服務(wù)

為了提高推送服務(wù)的注冊率,我選擇在Application的onCreate中初始化推送服務(wù)

BaseActivity.java

packagescut.carson_ho.demo_mipush;importandroid.app.ActivityManager;importandroid.app.Application;importandroid.content.Context;importandroid.os.Process;importcom.xiaomi.mipush.sdk.MiPushClient;importjava.util.List;/**

* Created by Carson_Ho on 16/10/26.

*///主要要繼承ApplicationpublicclassBaseActivityextendsApplication{// 使用自己APP的ID(官網(wǎng)注冊的)privatestaticfinalString APP_ID ="2882303761517520369";// 使用自己APP的Key(官網(wǎng)注冊的)privatestaticfinalString APP_KEY ="5401752085369";//為了提高推送服務(wù)的注冊率资昧,我建議在Application的onCreate中初始化推送服務(wù)//你也可以根據(jù)需要酬土,在其他地方初始化推送服務(wù)@OverridepublicvoidonCreate(){super.onCreate();if(shouldInit()) {//注冊推送服務(wù)//注冊成功后會向DemoMessageReceiver發(fā)送廣播// 可以從DemoMessageReceiver的onCommandResult方法中MiPushCommandMessage對象參數(shù)中獲取注冊信息MiPushClient.registerPush(this, APP_ID, APP_KEY);? ? ? ? }? ? }//通過判斷手機(jī)里的所有進(jìn)程是否有這個App的進(jìn)程//從而判斷該App是否有打開privatebooleanshouldInit(){//通過ActivityManager我們可以獲得系統(tǒng)里正在運(yùn)行的activities//包括進(jìn)程(Process)等、應(yīng)用程序/包格带、服務(wù)(Service)撤缴、任務(wù)(Task)信息。ActivityManager am = ((ActivityManager) getSystemService(Context.ACTIVITY_SERVICE));? ? ? ? List processInfos = am.getRunningAppProcesses();? ? ? ? String mainProcessName = getPackageName();//獲取本App的唯一標(biāo)識intmyPid = Process.myPid();//利用一個增強(qiáng)for循環(huán)取出手機(jī)里的所有進(jìn)程for(ActivityManager.RunningAppProcessInfo info : processInfos) {//通過比較進(jìn)程的唯一標(biāo)識和包名判斷進(jìn)程里是否存在該Appif(info.pid == myPid && mainProcessName.equals(info.processName)) {returntrue;? ? ? ? ? ? }? ? ? ? }returnfalse;? ? }}

注意要在Android.manifest.xml里的application里加入

android:name=".BaseActivity"

這樣在應(yīng)用初始化時是第一個加載BaseActivity.java類文件的

如下圖:

示意圖

步驟4:設(shè)置子類繼承PushMessageReceiver叽唱,并復(fù)寫相關(guān)推送消息的方法

Mipush_Broadcast.java

packagescut.carson_ho.demo_mipush;importandroid.content.Context;importcom.xiaomi.mipush.sdk.ErrorCode;importcom.xiaomi.mipush.sdk.MiPushClient;importcom.xiaomi.mipush.sdk.MiPushCommandMessage;importcom.xiaomi.mipush.sdk.MiPushMessage;importcom.xiaomi.mipush.sdk.PushMessageReceiver;/**

* Created by Carson_Ho on 16/10/26.

*/publicclassMipush_BroadcastextendsPushMessageReceiver{//透傳消息到達(dá)客戶端時調(diào)用//作用:可通過參數(shù)message從而獲得透傳消息腹泌,具體請看官方SDK文檔@OverridepublicvoidonReceivePassThroughMessage(Context context, MiPushMessage message){//打印消息方便測試System.out.println("透傳消息到達(dá)了");? ? ? ? System.out.println("透傳消息是"+message.toString());? ? }//通知消息到達(dá)客戶端時調(diào)用//注:應(yīng)用在前臺時不彈出通知的通知消息到達(dá)客戶端時也會回調(diào)函數(shù)//作用:通過參數(shù)message從而獲得通知消息,具體請看官方SDK文檔@OverridepublicvoidonNotificationMessageArrived(Context context, MiPushMessage message){//打印消息方便測試System.out.println("通知消息到達(dá)了");? ? ? ? System.out.println("通知消息是"+message.toString());? ? }//用戶手動點(diǎn)擊通知欄消息時調(diào)用//注:應(yīng)用在前臺時不彈出通知的通知消息到達(dá)客戶端時也會回調(diào)函數(shù)//作用:1. 通過參數(shù)message從而獲得通知消息尔觉,具體請看官方SDK文檔//2. 設(shè)置用戶點(diǎn)擊消息后打開應(yīng)用 or 網(wǎng)頁 or 其他頁面@OverridepublicvoidonNotificationMessageClicked(Context context, MiPushMessage message){//打印消息方便測試System.out.println("用戶點(diǎn)擊了通知消息");? ? ? ? System.out.println("通知消息是"+ message.toString());? ? ? ? System.out.println("點(diǎn)擊后,會進(jìn)入應(yīng)用");? ? }//用來接收客戶端向服務(wù)器發(fā)送命令后的響應(yīng)結(jié)果。@OverridepublicvoidonCommandResult(Context context, MiPushCommandMessage message){? ? ? ? String command = message.getCommand();? ? ? ? System.out.println(command );if(MiPushClient.COMMAND_REGISTER.equals(command)) {if(message.getResultCode() == ErrorCode.SUCCESS) {//打印信息便于測試注冊成功與否System.out.println("注冊成功");? ? ? ? ? ? }else{? ? ? ? ? ? ? ? System.out.println("注冊失敗");? ? ? ? ? ? }? ? ? ? }? ? }//用于接收客戶端向服務(wù)器發(fā)送注冊命令后的響應(yīng)結(jié)果芥吟。@OverridepublicvoidonReceiveRegisterResult(Context context, MiPushCommandMessage message){? ? ? ? String command = message.getCommand();? ? ? ? System.out.println(command );if(MiPushClient.COMMAND_REGISTER.equals(command)) {if(message.getResultCode() == ErrorCode.SUCCESS) {//打印日志:注冊成功System.out.println("注冊成功");? ? ? ? ? ? }else{//打印日志:注冊失敗System.out.println("注冊失敗");? ? ? ? ? ? }? ? ? ? }else{? ? ? ? ? ? System.out.println("其他情況"+message.getReason());? ? ? ? }? ? }}

具體設(shè)置請看官方SDK文檔侦铜,這里只給出最簡單Demo,不作過多描述

步驟5:在AndroidManifest文件里面配置好權(quán)限钟鸵、注冊Service和BroadcastReceiver

AndroidManifest.xml

//相關(guān)權(quán)限//注意這里.permission.MIPUSH_RECEIVE是自身app的包名//注意這里.permission.MIPUSH_RECEIVE是自身app的包名//注意要初始化BaseActivity.java類//注冊廣播BroadcastReceiver和Service? ? //都是靜態(tài)注冊钉稍,因?yàn)橐L期處在后臺運(yùn)行? ? //注:共是3個廣播接收器和4個服務(wù),其中包括繼承了PushMessageReceiver的DemoMessageReceiver? ? //4個后臺服務(wù)//此service必須在3.0.1版本以后(包括3.0.1版本)加入//此service必須在2.2.5版本以后(包括2.2.5版本)加入//3個廣播//繼承了PushMessageReceiver的DemoMessageReceiver的廣播注冊

步驟6:根據(jù)需要設(shè)置一系列的推送設(shè)置棺耍,如用戶別名贡未、標(biāo)簽等等

此處是簡單Demo,所以不作過多的設(shè)置

更多設(shè)置請回看上方官方Demo解析

運(yùn)行結(jié)果

測試成功結(jié)果

好了,客戶端的代碼寫好后俊卤,可以去小米官網(wǎng)測試一下消息推送了

步驟1:在小米官網(wǎng)的消息推送里選擇你創(chuàng)建的應(yīng)用嫩挤,然后點(diǎn)擊“推送工具”

點(diǎn)擊推送工具

步驟2:設(shè)置推送消息的相關(guān)信息

可進(jìn)行的配置非常全面,基本上能滿足推送的需求

設(shè)置推送消息

設(shè)置推送消息

推送的結(jié)果

消息到達(dá)客戶端

測試結(jié)果

測試結(jié)果:收到的信息

點(diǎn)擊通知欄消息后

4. Demo下載地址

Carson的Github:Demo_MiPush

5. 關(guān)于對小米推送的思考(問題)

上述說的小米推送看似簡單:初始化推送服務(wù) + 相關(guān)推送設(shè)置消恍。但是岂昭,好的代碼不僅能在正常情況下工作,還應(yīng)該充分考慮失敗情況狠怨。那么约啊,有什么樣的失敗情況需要我們考慮呢?

背景:在這個初始化推送服務(wù)的過程中佣赖,是需要聯(lián)系小米推送的服務(wù)器來申請reg id(即推送token)恰矩。

沖突:初始化過程可能失敗:網(wǎng)絡(luò)問題(沒網(wǎng)or網(wǎng)絡(luò)信號弱)憎蛤、服務(wù)器問題導(dǎo)致初始化失敗外傅。那么,當(dāng)失敗以后蹂午,該什么時候再次進(jìn)行初始化呢栏豺?

小米推送的Demo里并沒有相關(guān)措施解決這個問題

解決方案:在初始化失敗的情況下提供重試機(jī)制,直到初始化成功(可以通過檢測是否已經(jīng)拿到推送token來確定)豆胸,問題解決的邏輯如下:

解決邏輯

具體代碼在這里就不作過多描述奥洼,如果你希望獲得含注冊重試機(jī)制的小米推送源代碼,請?jiān)谠u論留下你的郵箱晚胡,我將親自發(fā)送到你的郵箱

知識點(diǎn)涵蓋:網(wǎng)絡(luò)數(shù)據(jù)的檢測 & 廣播接收器

具體請看我寫的另外兩篇文章:

Android:BroadcastReceiver廣播接收器最全面解析

Android:檢測網(wǎng)絡(luò)狀態(tài)&監(jiān)聽網(wǎng)絡(luò)變化

總結(jié)

全面考慮到所有異常問題并恰當(dāng)?shù)剡M(jìn)行處理才能真正體現(xiàn)程序猿的功力灵奖,希望大家做擼代碼的時候不要只做代碼的搬運(yùn)工,純粹寫代碼并不會讓你成長估盘,關(guān)鍵在于思考瓷患。

6. 總結(jié)

相信大家已經(jīng)非常了解如何在你的Android應(yīng)用中集成小米推送

接下來,我會繼續(xù)介紹具體如何在Android中關(guān)于推送的消息遣妥,請看

史上最全解析Android消息推送解決方案

Android推送:第三方消息推送平臺詳細(xì)解析

接下來擅编,我會繼續(xù)介紹具體如何在Android應(yīng)用中集成相應(yīng)的第三方推送功能(華為推送、極光推送箫踩、個推等等)爱态,有興趣可以繼續(xù)關(guān)注Carson_Ho的安卓開發(fā)筆記

請點(diǎn)贊!因?yàn)槟愕墓膭钍俏覍懽鞯淖畲髣恿Γ?/p>

相關(guān)文章閱讀

Android開發(fā):最全面境钟、最易懂的Android屏幕適配解決方案

Android開發(fā):Handler異步通信機(jī)制全面解析(包含Looper锦担、Message Queue)

Android開發(fā):頂部Tab導(dǎo)航欄實(shí)現(xiàn)(TabLayout+ViewPager+Fragment)

Android開發(fā):底部Tab菜單欄實(shí)現(xiàn)(FragmentTabHost+ViewPager)

Android開發(fā):JSON簡介及最全面解析方法!

Android開發(fā):XML簡介及DOM、SAX慨削、PULL解析對比

作者:Carson_Ho

鏈接:http://www.reibang.com/p/b1134bebc2d4

來源:簡書

著作權(quán)歸作者所有洞渔。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)套媚,非商業(yè)轉(zhuǎn)載請注明出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末磁椒,一起剝皮案震驚了整個濱河市堤瘤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌衷快,老刑警劉巖宙橱,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蘸拔,居然都是意外死亡师郑,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門调窍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宝冕,“玉大人,你說我怎么就攤上這事邓萨〉乩妫” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵缔恳,是天一觀的道長宝剖。 經(jīng)常有香客問我,道長歉甚,這世上最難降的妖魔是什么万细? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮纸泄,結(jié)果婚禮上赖钞,老公的妹妹穿的比我還像新娘。我一直安慰自己聘裁,他們只是感情好雪营,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著衡便,像睡著了一般献起。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上镣陕,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天征唬,我揣著相機(jī)與錄音,去河邊找鬼茁彭。 笑死,一個胖子當(dāng)著我的面吹牛扶歪,可吹牛的內(nèi)容都是我干的理肺。 我是一名探鬼主播摄闸,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼妹萨!你這毒婦竟也來了年枕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤乎完,失蹤者是張志新(化名)和其女友劉穎熏兄,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體树姨,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡摩桶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了帽揪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片硝清。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖转晰,靈堂內(nèi)的尸體忽然破棺而出芦拿,到底是詐尸還是另有隱情,我是刑警寧澤查邢,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布蔗崎,位于F島的核電站,受9級特大地震影響扰藕,放射性物質(zhì)發(fā)生泄漏缓苛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一实胸、第九天 我趴在偏房一處隱蔽的房頂上張望他嫡。 院中可真熱鬧,春花似錦庐完、人聲如沸钢属。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽淆党。三九已至,卻和暖如春讶凉,著一層夾襖步出監(jiān)牢的瞬間染乌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工懂讯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留荷憋,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓褐望,卻偏偏與公主長得像勒庄,于是被迫代替她去往敵國和親串前。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容