上一次折腾PUSH服务已经好几个月了,终于抽空重构了(APNS:Apple Push Notification Service,我谈的其实只是构建一个中间平台去调用apns服务)。
先介绍下之前所做的push服务的设计模型
当初的设计比较简单,将压力全部放在了推送消息处理线程上,线程不仅需要建立apns服务器的通信,也需要对广播消息进行转换。对于单播消息而言性能似乎还可以接受,然而对广播消息处理,线程的处理性能明显降低。因为apns服务是不支持广播消息的,进行广播必须要获取到所有广播对象的devicetoken,这就需要线程去批量获取推送对象列表,并将这个列表组成单播message,而后批量发送。这样的设计将消息处理和发送耦合在了一起,无法并行处理。虽然通过异步通信的方式实现了批量发送,但是总体而言性能还是很低的。
之前设计最根本的的问题是耦合了广播消息本身的处理和发送的处理,发挥不了多线程处理的性能。这次重构,主要的性能优化就是,区分了推送请求队列和推送消息队列,请求处理线程只专注对推送请求的处理,生成可用于直接发送的推送报文,并均衡负载到推送消息队列上。推送线程则专注于推送本身,每个线程hold一个与apns的连接,发送推送报文。
将push请求处理和push发送处理分开,也给系统扩展提供了更多的可能性。单服务器可以设置多个请求队列、多个发送队列,每个队列允许多个线程进行监听处理。也可以将推送请求处理和发送处理分开部署,并进行均衡负载。
废话了这么多,最后再戳下漏洞:由于没有区分消息级别,即时消息可能会淹没在队列中。对于实时性要求不是非常高的一般应用上面的模型还能凑合使用。大家有什么好的设计思路欢迎讨论。
欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
摘自李刚<疯狂android>备份学习使用
首先我们为该contentprovider定义一个工具类,该类中只是包含一个public static的常量,该工具类的代码如下:
import android.net.Uri; import android.provider.BaseColumns; public final class Words { //定义该contentprovider的authority public static final String AUTHORITY = "org.crazyit.providers.dictprovider"; //定义一个静态内部类 public static final class Word implements BaseColumns{ //定义content所允许操作的三个数据列 public static final String _ID = "_id"; public static final String WORD = "word"; public static final String DETAIL = "detail"; //定义该content提供服务的两个Uri public static final Uri DICT_CONTETN_URI = Uri.parse("content://" + AUTHORITY + "/words"); public static final Uri WORD_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/word"); } }
上面的工具类只是定义了一些简单的工具类,这个工具类的作用就是告诉其他应用程序,访问该contentprovider的一些常用入口。
数据库创建MyDatabaseHelper代码:
import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; public class MyDatabaseHelper extends SQLiteOpenHelper{ private final String CREATE_TABLE_SQL= "create table dict(_id integer primary key autoincrement, word , detail)"; public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_TABLE_SQL); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { System.out.println("--------onUpdate Called--------" + oldVersion + "----->" + newVersion); } }
接下来我们开发一个contentprovider的子类,并重写其中的增、删、改、查等方法,类代码如下。
import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import com.dictprovider.content.Words; import com.mydatabasehelper.database.MyDatabaseHelper; public class DictProvider extends ContentProvider{ private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); private static final int WORDS = 1; private static final int WORD = 2; private MyDatabaseHelper dbOpenHelper; static{ matcher.addURI(Words.AUTHORITY, "words", WORDS); matcher.addURI(Words.AUTHORITY, "word/#", WORD); } //第一次调用该DictProvider时,系统先创建DictProvider对象,并回调该方法 @Override public boolean onCreate() { dbOpenHelper = new MyDatabaseHelper(this.getContext(), "myDict.db3", null, 1); return true; } @Override public Uri insert(Uri uri, ContentValues values) { //获得数据库实例 SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); long rowId = db.insert("dict", Words.Word._ID, values); //如果插入成功则返回uri if(rowId > 0){ //在已有的Uri的后面追加ID数据 Uri wordUri = ContentUris.withAppendedId(uri, rowId); //通知数据已经改变 getContext().getContentResolver().notifyChange(wordUri, null); return wordUri; } return null; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); //记录所删除的记录数 int num = 0; //对uri进行匹配 switch(matcher.match(uri)){ case WORDS:{ num = db.delete("dict", selection, selectionArgs); break; } case WORD:{ long id = ContentUris.parseId(uri); String where = Words.Word._ID + "=" + id; //如果原来的where子句存在,拼接where子句 if(selection != null && !selection.equals("")){ where = where + " and " + selection; } num = db.delete("dict", where, selectionArgs); break; } } getContext().getContentResolver().notifyChange(uri, null); return num; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); int num = 0; switch(matcher.match(uri)){ case WORDS:{ num = db.update("dict", values, selection, selectionArgs); break; } case WORD:{ long id = ContentUris.parseId(uri); String where = Words.Word._ID + "=" + id; if(selection != null && !"".equals(selection)){ where = where + " and " + selection; } num = db.update("dict", values, where, selectionArgs); break; } } getContext().getContentResolver().notifyChange(uri, null); return num; } @Override public String getType(Uri uri) { switch(matcher.match(uri)){ case WORDS:{ return "vnd.android.cursor.dir/org.crazyit.dict"; } case WORD:{ return "vnd.android.cursor.item/org.crazyit.dict"; } } return null; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); switch(matcher.match(uri)){ case WORDS:{ return db.query("dict", projection, selection, selectionArgs, null, null, sortOrder); } case WORD:{ long id = ContentUris.parseId(uri); String where = Words.Word._ID + "=" + id; if(selection != null && !selection.equals("")){ where = where + " and " + selection; } return db.query("dict", projection, selection, selectionArgs, null, null, sortOrder); } } return null; } }
上面的dictProvider类很简单,它除了继承系统的contentprovider之外,还实现了操作数据的增、删、改、查等方法,那木该contentprovider就开发完成了。
接下来需要在androidmanifest.xml文件中注册该contentprovider,这就需要在androidmanifest.xml文件中增加如下配置片段:
<provider android:name="com.dictprovider.provider.DictProvider" android:authorities="org.crazyit.providers.dictprovider"/>
至此,暴露生词本数据的contentprovider开发完成。为了测试该contentprovider的开发是否成功,接下来再开发一个应用程序,该应用程序将会通过contentresolver来操作生词本中的数据。
该程序同样提供了添加生词、查询生词的功能,只是改程序并不保存数据,而是访问前面dictProvider所共享的数据,下面是contentresolver的类的代码。
import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import android.app.Activity; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import com.dictprovider.content.Words; import com.dictprovider.observer.WordObserver; public class MainActivity extends Activity { ContentResolver contentResolver; Button insert = null; Button search = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //获取系统contentresolver对象 contentResolver = getContentResolver(); getContentResolver().registerContentObserver(Uri.parse("content://" + Words.AUTHORITY), true, new WordObserver(this,new Handler())); insert = (Button)findViewById(R.id.insert); search = (Button)findViewById(R.id.search); insert.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String word = ((EditText)findViewById(R.id.word)).getText().toString(); String detail = ((EditText)findViewById(R.id.detail)).getText().toString(); //插入生词记录 ContentValues values = new ContentValues(); values.put(Words.Word.WORD, word); values.put(Words.Word.DETAIL, detail); contentResolver.insert(Words.Word.DICT_CONTETN_URI, values); Toast.makeText(MainActivity.this, "insert success", Toast.LENGTH_SHORT).show(); } }); search.setOnClickListener(new OnClickListener() { //获取用户输入 @Override public void onClick(View v) { String key = ((EditText)findViewById(R.id.key)).getText().toString(); //执行查询 Cursor cursor = contentResolver.query( Words.Word.DICT_CONTETN_URI, null, "word like ? or detail like ?", new String[]{"%" + key + "%", "%" + key + "%"}, null); //创建一个bundle对象 Bundle data = new Bundle(); data.putSerializable("data", converCursorToList(cursor)); Intent intent = new Intent(MainActivity.this, ResultActivity.class); intent.putExtras(data); startActivity(intent); } }); } private ArrayList<Map<String, String>> converCursorToList(Cursor cursor){ ArrayList<Map<String, String>> result = new ArrayList<Map<String,String>>(); while(cursor.moveToNext()){ Map<String, String> map = new HashMap<String, String>(); map.put(Words.Word.WORD, cursor.getString(1)); map.put(Words.Word.DETAIL, cursor.getString(2)); result.add(map); } return result; } }
工程代码见附件
两个double数组相互赋值:
double [][] a = new double[10][10];
double [][] b = new double[10][10];
a = b;//a 和 b 是指向a数组和b数组的地址的指针,此操作是把指向a数组的指针指向了b数组,此后a和b指向同一个数组,若改变a的值,b的值也会改变。若改变b的值,a的值也会同变。
//若想把数组b的值赋值给数组a,又把他俩当成互不相干的两个数组,需要把数组b的值逐个赋值给数组a
for(int i=0;i<10;i++)
{
for(int j=0;j<0;j++){
a[i][j] = b[i][j];
}
}