編輯:關於Android編程
https://www.jpush.cn/common/
推送比較使用,很多軟件有需要,所以在這個點拿出來多講講,我們本節課奇會講到
AndroidPn推送 JPush推送 JPush實現聊天
一.推送的原理
既然要使用推送,首先我們還是要來了解一下原理,因為我們不僅要使用JPush來實現推送,我們還得自己來實現一個推送,有因必有果,哦彌陀佛!!
推送的方式有兩種,大家使用Git的時候也耳熟能詳
push
服務端有消息的時候主動向客戶端推送消息
pull
客戶端向服務器拉取消息,沒有消息的話不做操作
但是我們一般也不會去用pull方式,看JPush這個名字就知道,用的是push,因為pull去拉的話,就像ViewGroup要遍歷一遍所有的View一樣,麻煩,所有又費流又費電量,push推送是服務端要發什麼消息就直接發過來了,所有要方便些許
而推送的方式也有很多種,我這裡列舉幾種
C2DM雲端推送
Google官方的一個輕量級服務,依賴官方的C2DM服務器,優勢在於可以直接和服務器進行通訊,但是我們基本上是無視他的,為什麼?我大天朝
MQTT協議
這個我們也可以忽略,因為這個可以說技術積累不夠吧,很不穩定,雖然很小,但是要是一個推送都不穩定,那就成了雞肋的東西了
RSMB實現推送
我們這裡用一張圖來簡單說明下
這個協議我們不做太多的概括
XMPP協議
XMPP協議可謂鼎鼎大名,基本XML的協議,是一個開源的協議,采用的通信模式是C/S(客戶端/服務端),而且操作起來相對來說比較簡單,同樣的,因為基於XML實現,所以數據傳輸格式也是XML,分布式的特點,這個協議我們就藥來多唠叨幾句了
他的工作原理
客戶端連接服務端——服務端進行認證——客戶端請求操作——交互
我們等下講會說一個AndroidPn的推送就是基於XMPP協議的
第三方(JPush,BaiDu之類)
現在各大平台都有自己的推動服務了,百度,小米,極光,等等等等….
二.AndroidPn實現推送
AndroidPn大家可能用的還是相對要少一點吧
地址: https://sourceforge.net/projects/androidpn/
我們簡單的來聊聊這個框架
他是基於XMPP協議 包含完整的服務端和客戶端 基於openfire開源工程
那我們來實現以下,其實實現起來還是稍微有點復雜的,我們要進行以下的幾個步驟
安裝TomCat本地服務器(模擬服務器) 下載AndroidPn源碼部署在TomCat上 實現推送後台 集成AndroidPn客戶端代碼 通過浏覽器完成推送
我們慢工出細活,一步步來實現
1.安裝TomCat服務器
這裡我們就不重復講了,有需要的看這篇十分簡單的實現TomCat服務器的搭建
Android服務器——TomCat服務器的搭建
我們開啟服務器
我們只要在浏覽器能看到
就說明配置完成了
2.下載AndroidPn源碼部署在TomCat上
官網地址上面說了
地址: https://sourceforge.net/projects/androidpn/
我們直接進去可以看到
我們直接點擊Download就可以下載了,他下面有一段描述
An open source project to provide push notification support for Android -- a xmpp based notification server and a client tool kit.
意思就是說:一個開源項目提供推送式通知支持Android——基於xmpp的通知服務器和客戶端工具;
我們下載好之後就直接解壓
我們可以直接進入bin文件裡面啟動服務器
到這裡,我們的服務器是啟動好了,我們可以直接在浏覽器裡面輸入
http://127.0.0.1:7070/
哈哈,到這裡,我們基本的環境就已經部署了,這裡我說明一下,AndroidPn的代碼下載之後解壓隨便解壓在什麼地方,以為之前已經啟動了TomCat的原因,所以他會直接依賴
3.實現AndroidPn Client代碼
客戶端的實現,其實還是分了若干個步驟的,我們一點點來書寫,首先我們需要下載一個asmack.jar,用於XMPP傳輸協議的實現
asmack官網:https://github.com/Flowdalic/asmack asmack下載地址:http://asmack.freakempire.de/
這裡這麼多,我就隨便下載了一個最新的
好了,我們直接新建一個項目——AndroidPn
然後開始部署了,首先肯定是把jar包放在libs目錄下,接著,我們添加網絡權限
最後需要配置我們的秘鑰了,在res目錄下新建一個raw文件夾,在文件夾下新建一個android.properties文件,添加
apiKey=1234567890
xmppHost=192.168.1.100(你的本地IP)
xmppPort=5222
這些都做完了的話,我們可以直接去清單文件裡面注冊服務,我們的服務從哪裡來?看這裡
這是前輩寫好的類,我們可以直接拿來用,我會提供這個Demo給大家下載的,好了,我們可以去注冊了
我們回到MainActivity裡面
package com.lgl.androidpn;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.lgl.androidpn.client.ServiceManager;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ServiceManager serviceManager = new ServiceManager(this);
//設置通知欄圖標
serviceManager.setNotificationIcon(R.mipmap.ic_launcher);
//啟動服務
serviceManager.startService();
}
}
只要我們啟動程序,這個服務就啟動了,在此之前,我們還得家點權限
現在我們可以運行一下程序,毫無疑問,我們現在的程序是空的,但是在後台可以看到活動
這個時候我們就可以去實現推送了
然後我們接收到了
這裡也說明一下,這個AndroidPn小案例也只是告訴大家這個推送的實現原理,以及我們親手去實現了一遍推送之後得到的寶貴經驗,並不推薦使用這個,API過時了,有點蛋疼,而且2010年就已經停止更新了,不推薦使用,我們只要了解原理就可以了
三.JPush實現推送
終於到我們的主角了Jpush極光推送,說實話這個也有點坑
官方地址: https://www.jpush.cn/common/
我們根據官網的API文檔來講解
1.集成JPush推送環境
JPush我就不再多做什麼介紹了,我簡單說一下他的有點(來自網絡)
SDK采用自定義的協議保持長連接,電量,流量都相對要少 服務器的架構比較先進 高並發可擴展性的雲服務
最後加一句,還是有點坑,哈哈,不過正常使用絕對是沒有問題的
第三方SDK現在基本上已經不用一步步來了,我們直接新建一個工程——LGLJPush
我們在後台新建一個應用
我們可以根據他的文檔來
http://docs.jpush.io/guideline/android_guide/
首先下載SDK
http://docs.jpush.io/resources/
然後添加權限
再增加一個自定義的權限
接著我們在application中進行各種注冊了
AS還是比較方便的,這裡我們只要修改一下我們自定義的廣播和開發者的key就可以,我們新建一個廣播——JPushReceiver配置上去就可以了
現在我們可以來初始化了,JPush的初始化需要在Application中進行,所以我們還要新建一個JPushApplication
package com.lgl.lgljpush;
import android.app.Application;
import cn.jpush.android.api.JPushInterface;
/**
* 初始化JPush
* Created by LGL on 2016/5/21.
*/
public class JPushApplication extends Application{
@Override
public void onCreate() {
super.onCreate();
JPushInterface.setDebugMode(true);
JPushInterface.init(this);
}
}
接著定義一下廣播
package com.lgl.lgljpush;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import cn.jpush.android.api.JPushInterface;
/**
* Jpsuh推送廣播
* Created by LGL on 2016/5/21.
*/
public class JPushReceiver extends BroadcastReceiver {
public static final String TAG = "JPushReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
String action = intent.getAction();
if (JPushInterface.ACTION_REGISTRATION_ID.equals(intent.getAction())) {
String regId = bundle.getString(JPushInterface.EXTRA_REGISTRATION_ID);
Log.d(TAG, "[MyReceiver] 接收Registration Id : " + regId);
} else if (JPushInterface.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())) {
Log.d(TAG, "[MyReceiver] 接收到推送下來的自定義消息: " + bundle.getString(JPushInterface.EXTRA_MESSAGE));
} else if (JPushInterface.ACTION_NOTIFICATION_RECEIVED.equals(intent.getAction())) {
Log.d(TAG, "[MyReceiver] 接收到推送下來的通知");
int notifactionId = bundle.getInt(JPushInterface.EXTRA_NOTIFICATION_ID);
Log.d(TAG, "[MyReceiver] 接收到推送下來的通知的ID: " + notifactionId);
} else if (JPushInterface.ACTION_NOTIFICATION_OPENED.equals(intent.getAction())) {
Log.d(TAG, "[MyReceiver] 用戶點擊打開了通知");
//打開自定義的Activity
} else if (JPushInterface.ACTION_RICHPUSH_CALLBACK.equals(intent.getAction())) {
Log.d(TAG, "[MyReceiver] 用戶收到到RICH PUSH CALLBACK: " + bundle.getString(JPushInterface.EXTRA_EXTRA));
//在這裡根據 JPushInterface.EXTRA_EXTRA 的內容處理代碼,比如打開新的Activity, 打開一個網頁等..
} else if (JPushInterface.ACTION_CONNECTION_CHANGE.equals(intent.getAction())) {
boolean connected = intent.getBooleanExtra(JPushInterface.EXTRA_CONNECTION_CHANGE, false);
Log.w(TAG, "[MyReceiver]" + intent.getAction() + " connected state change to " + connected);
} else {
Log.d(TAG, "[MyReceiver] Unhandled intent - " + intent.getAction());
}
}
}
這裡其實只要定義幾個就行,我照著官方的Demo就把所有的廣播都寫了一遍了。回到我們的控制台
我們點擊推送
OK,推送成功了,不過這裡要注意的是,我曾一度的被這個問題困擾
可能也怪自己太輕視了吧,於是翻閱了一下資料,根據他的錯誤找到了問題的所在,其實我們的lib類庫是需要加載在Gradle下的,所在,我們可以在build.gradle中的android根節點下配置
sourceSets.main{
jniLibs.srcDir 'libs'
}
從而綁定我們的類庫,我們看圖
OK,我們的極光推送就集成成功了
2.自定義推送
JPush給我們提供了一個比較高級的用法,就是可以在客戶端進行推送,需要我們做一些處理,具體是什麼呢,我們可以參照一下JPush官方提供的服務端文檔
http://docs.jpush.io/server/rest_api_v3_push/
文檔中提到,我們需要一個Authorization
通過appKey:masterSecret的組合Base64算法得到的一個數字,這個masterSecret在我們的控制台
在下面的文章中,可能涉及到很多JPush的術語,有些可能我沒有闡述到位的地方還是勞煩你多看看上面的文檔哦!
推送的必填是平台和設備,JPush 當前支持 Android, iOS, Windows Phone 三個平台的推送。其關鍵字分別為:”android”, “ios”, “winphone”。
我們舉個例子
推送到所有平台:
{ "platform" : "all" }
指定特定推送平台:
{ "platform" : ["android", "ios"] }
audience
推送設備對象,表示一條推送可以被推送到哪些設備列表。確認推送設備對象,JPush 提供了多種方式,比如:別名、標簽、注冊ID、分群、廣播等。
all
如果要發廣播(全部設備),則直接填寫 “all”,我們一般也是填all
OK,我們開始第一步,用Base64算法得到我們的秘鑰,網上搜索一下Base64的在線解碼
http://www1.tc711.com/tool/BASE64.htm
開始解碼
我們就可以得到秘鑰了
YzNlNDIyODdiZjQ4Y2NmYWQ4N2MwNDEyOmFkMmNiNDMxZmNmZDAwMWIwNzBjNTlhMSA=
這裡我們使用的是HttpClient的Post請求,所以你必須在Gradle的文件裡面加入
useLibrary 'org.apache.http.legacy'
看圖更加形象一點
我們寫個布局
很簡單,就一個輸入框和一個按鈕,我們要做的事情也很簡單,點擊發送按鈕之後就發送消息,通過開啟一個子線程去實現,大概的代碼
package com.lgl.lgljpush;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
public class MainActivity extends AppCompatActivity {
//發送按鈕
private Button btn_send;
//文本框
private EditText et_content;
//要發送的文本
private String text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_send = (Button) findViewById(R.id.btn_send);
et_content = (EditText) findViewById(R.id.et_content);
btn_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//拿到輸入框的內容
text = et_content.getText().toString();
//判斷不能為null
if (!TextUtils.isEmpty(text)) {
new Thread(runnable).start();
} else {
Toast.makeText(MainActivity.this, "請輸入文字", Toast.LENGTH_SHORT).show();
}
}
});
}
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
//Post初始化
String url = "https://api.jpush.cn/v3/push";
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
//添加頭部信息
httpPost.addHeader("Authorization", "Basic YzNlNDIyODdiZjQ4Y2NmYWQ4N2MwNDEyOjY4Zjg2MTIxMDM2YjNhODRmOWRhN2VhOA==");
//定義Json請求
httpPost.addHeader("Content-Type", "application/json");
//添加字段
JSONObject obj = new JSONObject();
obj.put("platform", "all");
obj.put("audience", "all");
JSONObject notification = new JSONObject();
JSONObject android = new JSONObject();
android.put("alert", text);
notification.put("android", android);
obj.put("notification", notification);
httpPost.setEntity(new StringEntity(obj.toString()));
HttpResponse response = httpClient.execute(httpPost);
//打印返回碼
Log.e("返回碼", response.getStatusLine().getStatusCode() + "");
} catch (JSONException e) {
e.printStackTrace();
Log.e("返回碼", "JSONException");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
Log.e("返回碼", "UnsupportedEncodingException");
} catch (ClientProtocolException e) {
e.printStackTrace();
Log.e("返回碼", "ClientProtocolException");
} catch (IOException e) {
e.printStackTrace();
Log.e("返回碼", "IOException");
} catch (StackOverflowError e) {
Log.e("返回碼", "StackOverflowError");
}
}
};
/**
* 發送消息 Post方式發送
*
* @param date
* @return
*/
private String sendMessage(String date) {
return "";
}
}
我自問我的代碼寫的還是邏輯很清晰的,相信你一定能看懂,好了,看下運行的返回碼
那就說明成功了,我們看一下運行的結果
這裡我用的是模擬器,所以不能輸入中文,我們可以看到我發送的消息是ddd,然後手機就接收到了,其實,我們可以根據設備進行推送,而且可以指定推送內容,這樣,是不是我們可以根據邏輯實現一個聊天的小應用?這裡本來想寫的。篇幅太多了,留著以後有時間寫吧,感興趣的可以自己去實現以下,根據注冊ID去發送消息,JPush也給我們提供了接口
Log.e("ID", JPushInterface.getRegistrationID(this));
Nexus 5x: 140fe1da9eab772de2c
什麼是RecyclerView RecyclerView是Android 5.0 ma
ExpandableTextView,可展開和收起的TextView,就像GooglePlay裡面顯示應用的描述那樣。項目地址:https://github.com/Ma
說來慚愧,MVP的架構模式已經在Android領域出現一兩年了,但是到今天自己才開始Android領域中的MVP架構征程。閒話不多說,開始吧!一、架構演變概述我記得我找
ActionBarDrawerToggle:在前一張中我們並沒有使用drawLayout.setDrawerListener(); 對應的參數對象就是Drawe