当前位置: 编程技术>移动开发
本页文章导读:
▪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
最新技术文章: