当前位置: 编程技术>移动开发
本页文章导读:
▪Core Data浅谈系列之8 : 关于并发 Core Data浅谈系列之八 : 关于并发有时候,我们需要有个worker thread来做一些密集型或者长耗时的任务,以避免阻塞住UI,给用户不好的体验。比如从网络上获取一批数据,然后解析它们,并将.........
▪ JNI入门完整详细示范 JNI入门完整详细示例mainActivity如下:
package c.c;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
import android.app.Activity;
/**
* JN.........
▪ Core Data浅谈系列之7 : 使用NSFetchedResultsController Core Data浅谈系列之七 : 使用NSFetchedResultsController上一篇讨论到添加球员信息后,球员列表没有及时得到修改。这是由于之前我们简单地使用了一个NSMutableArray来管理球员列表,需要我们额外.........
[1]Core Data浅谈系列之8 : 关于并发
来源: 互联网 发布时间: 2014-02-18
Core Data浅谈系列之八 : 关于并发
有时候,我们需要有个worker thread来做一些密集型或者长耗时的任务,以避免阻塞住UI,给用户不好的体验。比如从网络上获取一批数据,然后解析它们,并将其输出到存储文件中。这时候,由于数据层发生了变动,我们希望通知到主线程更新UI —— 这就涉及到Core Data的多线程特性。
有时候,我们需要有个worker thread来做一些密集型或者长耗时的任务,以避免阻塞住UI,给用户不好的体验。比如从网络上获取一批数据,然后解析它们,并将其输出到存储文件中。这时候,由于数据层发生了变动,我们希望通知到主线程更新UI —— 这就涉及到Core Data的多线程特性。
比如我们一直以来使用的Demo中,添加球员信息的AddPlayerViewController和显示球员列表的PlayerListViewController在进行CURD操作时都是在主ViewController的context中完成的,这通过维持一个属性cdViewController指向主ViewController来实现:
#pragma mark -
#pragma mark - UITableView Delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
Team *teamObject = [self.teamArray objectAtIndex:indexPath.row];
PlayerListViewController *playerListVC = [[[PlayerListViewController alloc] init] autorelease];
playerListVC.team = teamObject;
playerListVC.cdViewController = self;
[self.navigationController pushViewController:playerListVC animated:YES];
}
以及:#pragma mark -
#pragma mark - Player CURD
- (void)addPlayer:(id)sender
{
AddPlayerViewController *addPlayerVC = [[[AddPlayerViewController alloc] init] autorelease];
addPlayerVC.cdViewController = self.cdViewController;
addPlayerVC.team = self.team;
[self presentModalViewController:addPlayerVC animated:YES];
}
对于比较小的Demo,这么写代码是可以接受的,虽然也会觉得传递得有点长。
当程序的代码规模比较大,或者说处理的数据比较多时,我们可以通过引入并发特性来做一点优化。
通过创建临时的context来添加球员信息:
- (IBAction)addBtnDidClick:(id)sender
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSManagedObjectContext *tmpContext = [[NSManagedObjectContext alloc] init];
[tmpContext setPersistentStoreCoordinator:sharedPersistentStoreCoordinator];
// We don't check the user input.
Player *playerObject = [NSEntityDescription insertNewObjectForEntityForName:@"Player" inManagedObjectContext:tmpContext];
playerObject.name = self.nameTextField.text;
playerObject.age = [NSNumber numberWithInteger:[self.ageTextField.text integerValue]];
playerObject.team = self.team;
NSError *error = NULL;
if (tmpContext && [tmpContext hasChanges] && ![tmpContext save:&error]) {
NSLog(@"Error %@, %@", error, [error localizedDescription]);
abort();
}
dispatch_async(dispatch_get_main_queue(), ^{
[self dismissModalViewControllerAnimated:YES];
});
});
}
为了响应其它线程的变化,参考此文档,我们可以先监听消息,然后合并发生了的变化:[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mocDidSaveNotification:) name:NSManagedObjectContextDidSaveNotification object:nil];
- (void)mocDidSaveNotification:(NSNotification *)notification
{
NSManagedObjectContext *savedContext = [notification object];
if (savedContext == self.managedObjectContext) {
return ;
}
if (savedContext.persistentStoreCoordinator != self.persistentStoreCoordinator) {
return ;
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Merge changes from other context.\n");
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
});
}
这么做了之后,我们尝试添加一名球员,会得到如下错误信息:2013-01-21 09:56:08.300 cdNBA[573:617] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Illegal attempt to establish a relationship 'team' between objects in different contexts这是由于我们把主线程context中的team对象传递到临时创建的context中进行操作了。在Core Data的多线程环境中,我们只能传递objectID或者重新fetch:
addPlayerVC.teamID = [self.team objectID]; // ... playerObject.team = (Team *)[tmpContext objectWithID:self.teamID];这样可以执行过去,控制台输出:
2013-01-21 10:11:12.834 cdNBA[687:1b03] void _WebThreadLockFromAnyThread(bool), 0x83a91c0: Obtaining the web lock from a thread other than the main thread or the web thread. UIKit should not be called from a secondary thread. 2013-01-21 10:11:12.932 cdNBA[687:c07] Merge changes from other context.
第二行日志说明合并变化了,不过第一行告诉我们在非主线程里面访问了一些UI方面的东西。这是由于上面在global_queue里面访问了UITextField,把访问UI的代码提到外面即可。
BTW,在iOS 5以后,苹果提供了更为便捷有效的parent-child context机制,可以参见这里。
Brief Talk About Core Data Series, Part 8 : About Concurrency
Jason Lee @ Hangzhou
Blog : http://blog.csdn.net/jasonblog
Weibo : http://weibo.com/jasonmblog
[2] JNI入门完整详细示范
来源: 互联网 发布时间: 2014-02-18
JNI入门完整详细示例
mainActivity如下:
package c.c;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
import android.app.Activity;
/**
* JNI的HelloWorld示例
* 1 下载和安装cygwin.重要参考资料:
* http://www.cnblogs.com/playing/archive/2011/07/14/2106727.html
* 注意make组件的安装,该步骤没有截图,但和binutils,gcc,gcc-mingw,gdb
* 是很类似的
* 2 检查cygwin是否安装正确
* 通过命令行进入cygwin 输入cd $NDKROOT,若输出bash_profile中于ndk
* 配置相关的一行信息则表示安装成功.
* 注意这里的输入的命令是cd $NDKROOT,所以bash_profile文件中配置的
* 名称也应是NDKROOT.即两者保持一致
* 3 编译
* 3.1 进入cygwin,然后cd,然后空格,然后拖入android工程的完成路径.
* 例如:$ cd /cygdrive/d/workplace/JNITest
* 3.2 再执行命令$ $NDKROOT/ndk-build -B
* 即可生成.so文件
*
* 编写与JNI有关的HelloWorld的小例子,参考资料:
* http://blog.csdn.net/zhangjie201412/article/details/7297899
* 注意的问题:
* 1 JNI函数的名称格式
* Java_完整的Activity路径_方法名
* 比如此处:
* jstring Java_c_c_MainActivity_getString(JNIEnv *env,jobject jobj)
* 该问题在上面的网址中也有提及,要尤其注意
* 2 利用 System.loadLibrary()载入原生库时
* 参数为.c文件的名称,比如:
* System.loadLibrary("HelloWorld")
*/
public class MainActivity extends Activity {
private Button mButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
init();
}
private void init(){
mButton=(Button) findViewById(R.id.button);
mButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
String result=getString();
Toast.makeText(MainActivity.this, result, Toast.LENGTH_LONG).show();
}
});
}
//声明JNI函数函数
public native String getString();
//载入原生库
static {
System.loadLibrary("HelloWorld");
}
}
HelloWorld.c如下:
//HelloWorld.c
#include <string.h>
#include <jni.h>
jstring Java_c_c_MainActivity_getString(JNIEnv *env,jobject jobj)
{
return (*env)->NewStringUTF(env,"HelloWorld,JNI is good");
}
Android.mk如下:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := HelloWorld LOCAL_SRC_FILES := HelloWorld.c LOCAL_LDLIBS += -llog -ldl include $(BUILD_SHARED_LIBRARY)
Application.mk.mk如下:
APP_STL := gnustl_static APP_CPPFLAGS := -frtti -fexceptions APP_ABI := armeabi-v7a
main.xml如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="hello JNI"
/>
</RelativeLayout>
[3] Core Data浅谈系列之7 : 使用NSFetchedResultsController
来源: 互联网 发布时间: 2014-02-18
Core Data浅谈系列之七 : 使用NSFetchedResultsController
上一篇讨论到添加球员信息后,球员列表没有及时得到修改。这是由于之前我们简单地使用了一个NSMutableArray来管理球员列表,需要我们额外做一些变更通知。而在Core Data和UITableView之间,存在这一个名为NSFetchedResultsController的类为我们提供更多方便。
从很大程度上来看,NSFetchedResultsController是为了响应Model层的变化而设计的。
在使用NSFetchedResultsController之前,我们需要为其设置一个NSFetchRequest,且这个fetchRequest必须得有一个sortDescriptor,而过滤条件predicate则是可选的。
接着,还需要一个操作环境,即NSManagedObjectContext。
通过设置keyPath,就是将要读取的entity的(间接)属性,来作为section分类key。
之后,我们为其设置可选的cache名称,以避免执行一些重复操作。
最后,可以设置delegate,用来接收响应变化的通知。
#pragma mark -
#pragma mark - NSFetchedResultsController
- (NSFetchedResultsController *)fetchedResultsController
{
if (nil != _fetchedResultsController) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *playerEntity = [NSEntityDescription entityForName:@"Player" inManagedObjectContext:self.cdViewController.managedObjectContext];
[fetchRequest setEntity:playerEntity];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"age"ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"team == %@", self.team];
[fetchRequest setPredicate:predicate];
[fetchRequest setFetchBatchSize:20];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.cdViewController.managedObjectContext sectionNameKeyPath:nil cacheName:@"Players"];
_fetchedResultsController.delegate = self;
NSError *error = NULL;
if (![_fetchedResultsController performFetch:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return _fetchedResultsController;
}
这时候,我们将取消掉之前的playerArray,而是将self.fetchedResultsController作为UITableView的数据源:#pragma mark -
#pragma mark - UITableView DataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
staticNSString *cellIdentifier = @"TeamTableViewCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (nil == cell) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier] autorelease];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
Player *playerObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.imageView.backgroundColor = [UIColor redColor];
cell.textLabel.text = playerObject.name;
cell.detailTextLabel.text = [playerObject.age stringValue];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
为了在添加球员信息后,返回到上一个界面立即可以看到,我们需要重写对响应变化的代理函数。
这里有一份经典用法代码片段,不过Demo里采取的是简单有效的方法,因为不需要动画效果(并且适用于大批量数据的更新):
#pragma mark -
#pragma mark - NSFetchedResultsController Delegate
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.playerTable reloadData];
}
做完以上工作,在添加完球员信息后,UITableView立刻可以得到刷新。Brief Talk About Core Data Series, Part 7 : Using NSFetchedResultsController
Jason Lee @ Hangzhou
Blog : http://blog.csdn.net/jasonblog
Weibo : http://weibo.com/jasonmblog
最新技术文章: