Prove OrganizationServiceProxy is not Thread Safe - thread-safety

I want to prove that re-using instances of OrganizationServiceProxy between threads will cause problems.
This console app does not have a problem re-using the same instance of OrganizationServiceProxy between threads:
class Program
{
private static OrganizationServiceProxy Service { get; set; }
static void Main(string[] args)
{
Connect(); // Initializes Service
for (int i = 0; i < 100; i++)
{
int index = i;
Task.Run(() =>
{
for (int i2 = 0; i2 < 10; i2++)
{
try
{
Console.WriteLine("Creating" + index);
Entity record = new Entity("account");
record.Id = new Guid("4986e130-45f7-e411-9454-00155d91de01");
record["name"] = index + " - " + i2;
Service.Update(record);
Console.WriteLine("Created" + index);
}
catch (Exception e)
{ }
}
});
}
Console.ReadLine(); // the name of the record ends up as 99 - 9, which is right
}
/* Initialize Service */
private static bool Connect()
{
try
{
ClientCredentials cred = new ClientCredentials();
cred.UserName.UserName = #"r";
cred.UserName.Password = #"";
IServiceManagement<IOrganizationService> serviceManagement = ServiceConfigurationFactory.CreateManagement<IOrganizationService>(new Uri(#"/XRMServices/2011/Organization.svc"));
Service = new OrganizationServiceProxy(serviceManagement, cred);
var who = new Microsoft.Crm.Sdk.Messages.WhoAmIRequest(); // used to test the connection
var whoResponse = (Microsoft.Crm.Sdk.Messages.WhoAmIResponse)Service.Execute(who); // this fails if not connected
}
catch (Exception e)
{
Console.WriteLine("Connecting to CRM.\n" + e.Message + ((e.InnerException != null) ? "\n" + e.InnerException.Message : ""));
return false;
}
return true;
}
}
The SDK states that any instance members of OrganizationServiceProxy are not guaranteed to be thread safe.
How can I cause a problem with an OrganizationServiceProxy shared between threads?
What kinds of problem are to be expected?

I'm not sure I know the specific answer to your question, but something that is marked as not guaranteed of being thread-safe just means exactly that: It may be safe, but the author has not tested for it or specifically written any thread-safe code for those classes, and thus cannot guarantee thread safety.
I do know that thread-safety definitely comes into play with Plugins on the server. This is why you are not supposed to use local fields in a Plugin class. The Plugin engine re-uses the instances of your Plugin class instead of re-instantiating them for each execution. This means it is possible that your Plugin could execute with "old data" in those local fields that was used by the last thread, which could obviously cause all kinds of problems.

Related

Update Vaadin Progressbar with push asynchronously

I have a question when calculating the hash of files eg: md5, sha1, sha256 setting the progress in the progressBar through the asynchronous process
Thanks to Alejandro Duarte who shows a very practical example
https://github.com/newUserRepo/testbar/blob/issueBar/vaadin-upload/src/main/java/com/example/vaadinupload/ProcessingService.java#L44
the only way I can get it to work is that in method line 75, I put 4 milliseconds to process the task and the bar is updated, but it is too slow.
Yes, I do not sleep the Thread the application does not do the push correctly, and the changes are not reflected correctly to the client.
Another way that actually worked was with the Runnable interface and execute the heavy task in the run() method
#Override
public void run() {
calcularHash();
}
public void calcularHash() {
System.out.println("Path tmp archivo: " +
tmpPath.toFile().getAbsolutePath());
for(int f=0; f<hashType.size(); f++) {
try (InputStream bis = new
BufferedInputStream(Files.newInputStream(tmpPath))) {
t.initTime();
byte[] buffer = new byte[1024];
MessageDigest messageDigest =
MessageDigest.getInstance(hashType.get(f));
int dataRead = 0;
long largo = tmpPath.toFile().length();
Long acum = 0L;
while ((dataRead = bis.read(buffer)) != -1) {
messageDigest.update(buffer, 0, dataRead);
acum += dataRead;
Float per = ((float) acum / largo);
bar.setValue(per);
System.out.println(per * 100);
//textFieldPercent.setValue(Types.formatPercentaje(per *
100));
}
final byte[] bytesDigest = messageDigest.digest();
final StringBuilder sb = new StringBuilder();
for (int c = 0; c < bytesDigest.length; c++) {
sb.append(Integer.toString((bytesDigest[c] & 0xFF) + 0x100,
16).substring(1));
}
final String hashObtenido = sb.toString();
t.finishTime();
final String totalTime = t.getFinalTimeSec() + "seg " +
t.getFinalTimeMs() + "ms";
final String large = Types.getLargeFileFormat(largo);
System.out.println(hashObtenido);
ui.access(() -> {
checksumTransactions.initData(messageDigest.getAlgorithm(),
sb.toString(),large, totalTime);
});
//Files.delete(tmpPath); //fixme borrar desde el grid o UI
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
In the present picture I use a simple public void run () and the Progressbar is updated well
but we know that the application may have memory leaks and is not a good practice, the ideal would be to know how to execute that Background thread
I still do not know the best way to achieve this :$

Azure: How to move messages from poison queue to back to main queue?

I'm wondering if there is a tool or lib that can move messages between queues?
Currently, i'm doing something like below
public static void ProcessQueueMessage([QueueTrigger("myqueue-poison")] string message, TextWriter log)
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connString);
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();
CloudQueue queue = queueClient.GetQueueReference("myqueue");
queue.CreateIfNotExists();
var messageData = JsonConvert.SerializeObject(data, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
queue.AddMessage(new CloudQueueMessage(messageData));
}
As at (2018-09-11) version 1.4.1 of the Microsoft Azure Storage Explorer doesn’t have the ability to move messages from one Azure queue to another.
I blogged a simple solution to transfer poison messages back to the originating queue and thought it might save someone a few minutes. Obviously, you'll need to have fixed the error that caused the messages to end up in the poison message queue!
You’ll need to add a NuGet package reference to Microsoft.NET.Sdk.Functions :
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Queue;
void Main()
{
const string queuename = "MyQueueName";
string storageAccountString = "xxxxxx";
RetryPoisonMesssages(storageAccountString, queuename);
}
private static int RetryPoisonMesssages(string storageAccountString, string queuename)
{
CloudQueue targetqueue = GetCloudQueueRef(storageAccountString, queuename);
CloudQueue poisonqueue = GetCloudQueueRef(storageAccountString, queuename + "-poison");
int count = 0;
while (true)
{
var msg = poisonqueue.GetMessage();
if (msg == null)
break;
poisonqueue.DeleteMessage(msg);
targetqueue.AddMessage(msg);
count++;
}
return count;
}
private static CloudQueue GetCloudQueueRef(string storageAccountString, string queuename)
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(storageAccountString);
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();
CloudQueue queue = queueClient.GetQueueReference(queuename);
return queue;
}
Azure Storage Explorer version 1.15.0 can now do this as of 2020. https://github.com/microsoft/AzureStorageExplorer/issues/1064
Essentially Azure Storage doesn't support moving messages from one queue to another. You would need to do this on your own.
One way to implement moving the messages from one queue to another is by dequeuing the messages from the source queue (by calling GetMessages), read the contents of the message and then creating a new message in the target queue. This you can do via using Storage Client Library.
One tool that comes to my mind for moving messages is Cerebrata Azure Management Studio(paid product with 15 days free trial). It has this functionality.
As at (2018-09-11) version 1.4.1 of the Microsoft Azure Storage Explorer doesn't support moving queue messages.
Here's an updated version of Mitch's answer, using the latest Microsoft.Azure.Storage.Queue package. Simply create a new .NET Console application, add the above-mentioned package to it, and replace the contents of Program.cs with the following:
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Queue;
using System.Threading.Tasks;
namespace PoisonMessageDequeuer
{
class Program
{
static async Task Main(string[] args)
{
const string queuename = "MyQueueName";
string storageAccountString = "xxx";
await RetryPoisonMesssages(storageAccountString, queuename);
}
private static async Task<int> RetryPoisonMesssages(string storageAccountString, string queuename)
{
var targetqueue = GetCloudQueueRef(storageAccountString, queuename);
var poisonqueue = GetCloudQueueRef(storageAccountString, queuename + "-poison");
var count = 0;
while (true)
{
var msg = await poisonqueue.GetMessageAsync();
if (msg == null)
break;
await poisonqueue.DeleteMessageAsync(msg);
await targetqueue.AddMessageAsync(msg);
count++;
}
return count;
}
private static CloudQueue GetCloudQueueRef(string storageAccountString, string queuename)
{
var storageAccount = CloudStorageAccount.Parse(storageAccountString);
var queueClient = storageAccount.CreateCloudQueueClient();
var queue = queueClient.GetQueueReference(queuename);
return queue;
}
}
}
It's still pretty slow if you're working with >1000 messages though, so I'd recommend looking into batch APIs for higher quantities.
Here's a python script you may find useful. You'll need to install azure-storage-queue
queueService = QueueService(connection_string = "YOUR CONNECTION STRING")
for queue in queueService.list_queues():
if "poison" in queue.name:
print(queue.name)
targetQueueName = queue.name.replace("-poison", "")
while queueService.peek_messages(queue.name):
for message in queueService.get_messages(queue.name, 32):
print(".", end="", flush=True)
queueService.put_message(targetQueueName, message.content)
queueService.delete_message(queue.name, message.id, message.pop_receipt)
I just had to do this again and took the time to update my snipped to the new storage SDKs. See post at https://www.bokio.se/engineering-blog/how-to-re-run-the-poison-queue-in-azure-webjobs/ for more info.
Here is the code I used
using Azure.Storage.Queues;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace AzureQueueTransfer
{
internal class Program
{
// Need Read, Update & Process (full url, can create in storage explorer)
private const string sourceQueueSAS = "";
// Need Add (full url, can create in storage explorer)
private const string targetQueueSAS = "";
private static async Task Main(string[] args)
{
var sourceQueue = new QueueClient(new Uri(sourceQueueSAS));
var targetQueue = new QueueClient(new Uri(targetQueueSAS));
var queuedAny = true;
while (queuedAny)
{
Thread.Sleep(30000); // Sleep to make sure we dont build too much backlog so we can process new messages on higher prio than old ones
queuedAny = false;
foreach (var message in sourceQueue.ReceiveMessages(maxMessages: 32).Value)
{
queuedAny = true;
var res = await targetQueue.SendMessageAsync(message.Body);
Console.WriteLine($"Transfered: {message.MessageId}");
await sourceQueue.DeleteMessageAsync(message.MessageId, message.PopReceipt);
}
Console.WriteLine($"Finished batch");
}
}
}
}
To anyone coming here looking for a Node equivalent of #MitchWheats answer using an Azure Function.
import AzureStorage from 'azure-storage'
import { Context, HttpRequest } from '#azure/functions'
import util from 'util'
const queueService = AzureStorage.createQueueService()
queueService.messageEncoder = new AzureStorage.QueueMessageEncoder.TextBase64QueueMessageEncoder()
const deleteMessage = util.promisify(queueService.deleteMessage).bind(queueService)
const createMessage = util.promisify(queueService.createMessage).bind(queueService)
const getMessage = util.promisify(queueService.getMessage).bind(queueService)
export async function run (context: Context, req: HttpRequest): Promise<void> {
try {
const poisonQueue = (req.query.queue || (req.body && req.body.queue));
const targetQueue = poisonQueue.split('-')[0]
let count = 0
while (true) {
const message = await getMessage(poisonQueue)
if (!message) { break; }
if (message.messageText && message.messageId && message.popReceipt) {
await createMessage(targetQueue, message.messageText)
await deleteMessage(poisonQueue, message.messageId, message.popReceipt)
}
count++
}
context.res = {
body: `Replayed ${count} messages from ${poisonQueue} on ${targetQueue}`
};
} catch (e) {
context.res = { status: 500 }
}
}
To use the function you need to you provide connection information for the storage account used for your storage queues. This is provided as environment variables. Either you provide AZURE_STORAGE_ACCOUNT and AZURE_STORAGE_ACCESS_KEY, or AZURE_STORAGE_CONNECTION_STRING. More on this is available in the Azure Storage SDK docs.
Also wrote a few lines about it in this Medium article
Updated python based on Jon Canning's answer:
from azure.storage.queue import QueueServiceClient
queueService = QueueServiceClient.from_connection_string(conn_str="DefaultEndpointsProtocol=https;AccountName=<account>;AccountKey=<key>;EndpointSuffix=core.windows.net")
for queue in queueService.list_queues():
if "poison" in queue.name:
print(queue.name)
targetQueueName = queue.name.replace("-poison", "")
queue = queueService.get_queue_client(queue=queue.name)
targetQueue = queueService.get_queue_client(queue=targetQueueName)
while queue.peek_messages() :
messages = queue.receive_messages()
for msg in messages:
targetQueue.send_message(msg.content)
queue.delete_message(msg)
As Mikael Eliasson noted, the code in IGx89 answer is broken because
AddMessageAsync will overwrite some info on the message and then
DeleteMessagAsync will give a 404. The better solution is to copy the
values into a new message for AddMessageAsync
Please see enhanced version of RetryPoisonMesssages with an ability to specify only list of messages(instead of all in a queue) and allow to copy messages instead of move them.
It also logs success/failure for each message.
/// <param name="storageAccountString"></param>
/// <param name="queuename"></param>
/// <param name="idsToMove">If not null, only messages with listed IDs will be moved/copied</param>
/// <param name="deleteFromPoisonQueue">if false, messages will be copied; if true, they will be moved
///Warning: if queue is big, keeping deleteFromPoisonQueue=false can cause the same row
///from poisonqueue to be copied more than once(the reason is not found yet)</param>
/// <returns></returns>
private static async Task<int> RetryPoisonMesssages(string storageAccountString, string queuename, string[] idsToMove=null, bool deleteFromPoisonQueue=false)
{
var targetqueue = GetCloudQueueRef(storageAccountString, queuename);
var poisonQueueName = queuename + "-poison";
var poisonqueue = GetCloudQueueRef(storageAccountString, poisonQueueName);
var count = 0;
while (true)
{
var msg = await poisonqueue.GetMessageAsync();
if (msg == null)
{
Console.WriteLine("No more messages in a queue " + poisonQueueName);
break;
}
string action = "";
try
{
if (idsToMove == null || idsToMove.Contains(msg.Id))
{
var msgToAdd = msg;
if (deleteFromPoisonQueue)
{
//The reason is that AddMessageAsync will overwrite some info on the message and then DeleteMessagAsync will give a 404.
//The better solution is to copy the values into a new message for AddMessageAsync
msgToAdd = new CloudQueueMessage(msg.AsBytes);
}
action = "adding";
await targetqueue.AddMessageAsync(msgToAdd);
Console.WriteLine(action + " message ID " + msg.Id);
if (deleteFromPoisonQueue)
{
action = "deleting";
await poisonqueue.DeleteMessageAsync(msg);
}
Console.WriteLine(action + " message ID " + msg.Id);
}
}
catch (Exception ex)
{
Console.WriteLine("Error encountered when "+ action + " " + ex.Message + " at message ID " + msg.Id);
}
count++;
}
return count;
}

C3P0 Statement.close deadlock

Google returns lots of people with deadlock issues in C3P0, but none of the solutions appear to apply (most people suggest setting maxStatements = 0 and maxStatementsPerConnection = 0, both of which we have).
I am using a ComboPooledDataSource from C3P0, initialised as;
cpds = new ComboPooledDataSource();
cpds.setDriverClass("org.postgresql.Driver");
cpds.setJdbcUrl("jdbc:postgresql://" + host + ":5432/" + database);
cpds.setUser(user);
cpds.setPassword(pass);
My query function looks like;
public static List<Map<String, Object>> query(String q) {
Connection c = null;
Statement s = null;
ResultSet r = null;
try {
c = cpds.getConnection();
s = c.createStatement();
s.executeQuery(q);
r = s.getResultSet();
/* parse result set into results List<Map> */
return results;
}
catch(Exception e) { MyUtils.logException(e); }
finally {
closeQuietly(r);
closeQuietly(s);
closeQuietly(c);
}
return null;
}
No queries are returning, despite the query() method reaching the return results; line. The issue is that the finally block is hanging. I have determined that the closeQuietly(s); is the line that is hanging indefinitely.
The closeQuietly() method in question is as you would expect;
public static void closeQuietly(Statement s) {
try { if(s != null) s.close(); }
catch(Exception e) { MyUtils.logException(e); }
}
Why would this method hang on s.close()? I guess it is something to do with the way I am using C3P0.
My complete C3P0 configuration (almost entirely defaults) can be viewed here -> http://pastebin.com/K8XDdiBg
MyUtils.logException(); looks something like;
public static void logException(Exception e) {
StackTraceElement ste[] = e.getStackTrace();
String message = " !ERROR!: ";
for(int i = 0; i < ste.length; i++) {
if(ste[i].getClassName().contains("packagename")) {
message += String.format("%s at %s:%d", e.toString(), ste[i].getFileName(), ste[i].getLineNumber());
break;
}
}
System.err.println(message);
}
Everything runs smoothly if I remove the closeQuietly(s); line. Both closing the ResultSet and Connection object work without problem - apart from Connection starvation of course.

Failed to allocate timer 0: no slots left and unable to set dynamic row height

I have a screen which call a listfield.
public class Main_AllLatestNews extends MainScreen {
private Database_Webservice webservice;
private String[] title, category, date, imagepath = {"no picture", "no picture", "no picture", "no picture","no picture","no picture","no picture","no picture","no picture", "no picture"};
private int[] newsid;
private List_News newslist;
public Main_AllLatestNews(final boolean needdownload) {
super(USE_ALL_WIDTH);
webservice = new Database_Webservice();
add(new Custom_TopField(this, 0, -1, "", 1, 1));
add(new Custom_BottomField(this, 0));
add(new Custom_HeaderField(Config_GlobalFunction.latest));
if (needdownload){
Main.getUiApplication().pushScreen(
new Custom_LoadingScreen(30));
webservice.UpdateAllCatNews();
}else {
webservice.LoadtodayNews();
newsid = new int[webservice.news.size()];
title = new String[webservice.news.size()];
category = new String[webservice.news.size()];
date = new String[webservice.news.size()];
//imagepath = new String[webservice.news.size()];
for (int i = 0; i < webservice.news.size(); i++) {
newslist = (List_News) webservice.news.elementAt(i);
newsid[i] = newslist.getID();
title[i] = newslist.getNtitle();
category[i] = newslist.getNewCatName();
date[i] = newslist.getNArticalD();
//imagepath[i] = newslist.getImagePath();
}
add(new Custom_ListField(newsid, title, date, category, imagepath, true));
}
}
}
When I add custom_listfield then I get:
Failed to allocate timer 0: no slots left
Here is my listfield
public Custom_ListField(int newsid[], String title[], String date[],
String category[], String imagepath[], boolean islatest) {
super(0, ListField.MULTI_SELECT);
this.newsid = newsid;
setCallback(this);
setBackground(Config_GlobalFunction.loadbackground("background.png"));
this.islatest = islatest;
rows = new Vector();
for (int x = 0; x < title.length; x++) {
TableRowManager row = new TableRowManager();
titlelabel = new Custom_LabelField(title[x],
LabelField.USE_ALL_WIDTH | DrawStyle.LEFT);
titlelabel.setFont(Font.getDefault().derive(Font.BOLD, 23));
row.add(titlelabel);
datelabel = new Custom_LabelField(date[x], DrawStyle.ELLIPSIS
| LabelField.USE_ALL_WIDTH | DrawStyle.LEFT);
datelabel.setFont(Font.getDefault().derive(Font.BOLD, 18));
datelabel.setFontColor(Color.GRAY);
row.add(datelabel);
categorylabel = new Custom_LabelField(category[x],
DrawStyle.ELLIPSIS | LabelField.USE_ALL_WIDTH
| DrawStyle.LEFT);
categorylabel.setFont(Font.getDefault().derive(Font.BOLD, 18));
categorylabel.setFontColor(Color.RED);
row.add(categorylabel);
/*Bitmap imagebitmap = null;
if (!imagepath[x].toString().equals("no picture")) {
imagebitmap = Util_ImageLoader.loadImage(imagepath[x]);
} else {
imagepath[x] = "image_base.png";
imagebitmap = Bitmap.getBitmapResource(imagepath[x]);
}
image = new BitmapField(imagebitmap, Field.FIELD_HCENTER
| Field.FIELD_VCENTER);
row.add(image);*/
//setRowHeight(image.getBitmapHeight() + 10);
setRowHeight(70);
rows.addElement(row);
}
setSize(rows.size());
}
In this list, it will call 10 images or more. First I will check got link send to it else load local images. So the row height must be not same, however, it does not auto set row height for each row but set a same height to all row. I think out of memory because i call too many images? but I call in android also no problem.
This is my imageloader.
public class Util_ImageLoader {
public static Bitmap loadImage(String url) {
HttpConnection connection = null;
InputStream inputStream = null;
EncodedImage bitmap;
byte[] dataArray = null;
try {
// can use this for BlackBerry 5.0+ :
// connection = (HttpConnection) (new
// ConnectionFactory()).getConnection(url).getConnection();
connection = (HttpConnection) Connector
.open(url + Util_GetInternet.getConnParam(),
Connector.READ, true);
int responseCode = connection.getResponseCode();
if (responseCode == HttpConnection.HTTP_OK) {
inputStream = connection.openDataInputStream();
dataArray = IOUtilities.streamToBytes(inputStream);
}
} catch (Exception ex) {
} finally {
try {
inputStream.close();
connection.close();
} catch (Exception e) {
}
}
if (dataArray != null) {
bitmap = EncodedImage.createEncodedImage(dataArray, 0,
dataArray.length);
return bitmap.getBitmap();
} else {
return null;
}
}
}
1) What can I do to reduce the use of memory?
2) How to set different row height? I am set bitmap.getbitmapheight() but different bitmap will have different height.
//Updated//
I am running on simulator 9930 OS 7.0 and 8520 OS 5.0. Both also same result. Real Device cannot run because after signing the key also prompt the warning message try to Secure APi. I am completely commented all the images also same. I did not call neither online nor local image. I think is the data problem?
#AlanLai, can you tell us which device this is being run on, and which OS? Is it a simulator, or real hardware? Why don't you try commenting out the image completely. Don't show any images (network images, or local images). See if you still get the problem. Let's try to narrow down where exactly the code is that's causing your problem. Note: please post the information about which device you're testing on above, in the question, not as a comment response here. Thanks
How about to have only one TableRowManager and every drawRow set values with layout with specific values?
There's a lot of things you can do to reduce memory usage. For one, try to avoid keeping objects in memory longer than you really need them. One way this happens is if you keep member variables in your class, that could really be local variables in a method. Keeping member variables may lead to objects living longer than they need to, preventing the release of the memory they occupy.
Util_ImageLoader
For example, in Util_ImageLoader, you do almost all the work in the constructor. But then, you keep the result around (the Bitmap) in a static member variable (_bmap), which keeps it in memory. I know you do this so that you can call getBitmap(). But, you could change the class to be like this:
public class Util_ImageLoader {
public static Bitmap loadImage(String url) {
HttpConnection connection = null;
InputStream inputStream = null;
EncodedImage bitmap;
byte[] dataArray = null;
try {
// can use this for BlackBerry 5.0+ :
// connection = (HttpConnection) (new ConnectionFactory()).getConnection(url).getConnection();
connection = (HttpConnection) Connector.open(url + Util_GetInternet.getConnParam(), Connector.READ,
true);
int responseCode = connection.getResponseCode();
if (responseCode == HttpConnection.HTTP_OK) {
inputStream = connection.openDataInputStream();
dataArray = IOUtilities.streamToBytes(inputStream);
}
} catch (Exception ex) {
}
finally {
try {
inputStream.close();
connection.close();
} catch (Exception e) {
}
}
if (dataArray != null) {
bitmap = EncodedImage.createEncodedImage(dataArray, 0, dataArray.length);
return bitmap.getBitmap();
} else {
return null;
}
}
}
Because your Util_ImageLoader class doesn't really have any state associated with it, you can probably make it a class with just one static method. The static method does not require you to create an instance of Util_ImageLoader to use it. Just do this:
Bitmap img = Util_ImageLoader.loadImage("http://domain.com/path/image.png");
This allows the image that's loaded to be released as soon as the UI is done with it. The existing code keeps that image in memory for the life of the program.
Also, I replaced your custom code that uses a byte[] buffer, with the useful IOUtilities.streamtoBytes() method. Let the built-in libraries do the work of optimizing for you. Most of the time, they will do a pretty good job of that.
You also had some fixed point scaling code in your Util_ImageLoader class that wasn't doing anything. It was creating a scaled image of the same size as the original. So, I just removed that code. That can only help your memory usage. Image manipulation can be expensive.
Finally, I checked the web server return code (HTTP_OK) before I created any of the large objects needed for this method. If the network request fails, you certainly don't want to waste memory for no reason.
Custom_ListField
Again, you are keeping some objects around, possibly longer than needed. Let's go through your member variables:
private Bitmap bg = Bitmap.getBitmapResource("background.png"),
imagebitmap;
I don't know how many instances of Custom_ListField you will have in your app, but if you are going to assign bg to a constant app resource image, you should at least make it a static member variable, so that if there are 10 instances of Custom_ListField, you will only be keeping one bg variable in memory:
private static Bitmap bg = Bitmap.getBitmapResource("background.png"),
imagebitmap;
But, in your case, I don't think you need to keep that member variable at all. You can simply replace it where it's used, like this:
Background background = BackgroundFactory.createBitmapBackground(Bitmap.getBitmapResource("background.png"));
Then, the imagebitmap member can also be replaced with a local variable:
Bitmap imageBitmap = null;
if (!imagepath[x].toString().equals("no picture")) {
imageBitmap = Util_ImageLoader.loadImage(imagepath[x]);
imageBitmap = loader.getbitmap();
} else {
imagepath[x] = "image_base.png";
imageBitmap = Bitmap.getBitmapResource(imagepath[x]);
}
image = new BitmapField(imageBitmap, Field.FIELD_HCENTER | Field.FIELD_VCENTER);
imageBitmap only needs to be a local variable, not a member variable.
Debugging memory usage usually requires having the whole program, running, and profiling it. With only some of your code, I can't see all the other code that uses it. How many of each class is created is important? Which images are the large ones, and which are small? These are all questions you need to ask yourself to get your memory usage down.
But, hopefully, the general techniques I showed example of above can help you get started.
The problem was the Custom_ListField. This should extends listfield
instead of custom extends manager
public class Custom_ListField extends ListField {
private String[] title, category, date, imagepath;
private int[] newsid, catsid;
private List_News newslist;
private Bitmap imagebitmap[], localimage = Bitmap
.getBitmapResource("image_base.png");
private BrowserField webpage;
private Custom_BrowserFieldListener listener;
private boolean islatest;
private Vector content = null;
private ListCallback callback = null;
private int currentPosition = 0;
public Custom_ListField(Vector content, boolean islatest) {
this.content = content;
this.islatest = islatest;
newsid = new int[content.size()];
title = new String[content.size()];
category = new String[content.size()];
date = new String[content.size()];
imagepath = new String[content.size()];
catsid = new int[content.size()];
imagebitmap = new Bitmap[content.size()];
for (int i = 0; i < content.size(); i++) {
newslist = (List_News) content.elementAt(i);
newsid[i] = newslist.getID();
title[i] = newslist.getNtitle();
category[i] = newslist.getNewCatName();
date[i] = newslist.getNArticalD();
imagepath[i] = newslist.getImagePath();
if (!imagepath[i].toString().equals("no picture")) {
imagebitmap[i] = Util_ImageLoader.loadImage(imagepath[i]);
} else {
imagebitmap[i] = localimage;
}
catsid[i] = newslist.getCatID();
}
initCallbackListening();
this.setRowHeight(localimage.getHeight() + 10);
}
private void initCallbackListening() {
callback = new ListCallback();
this.setCallback(callback);
}
private class ListCallback implements ListFieldCallback {
public ListCallback() {
setBackground(Config_GlobalFunction
.loadbackground("background.png"));
}
public void drawListRow(ListField listField, Graphics graphics,
int index, int y, int width) {
currentPosition = index;
graphics.drawBitmap(
Display.getWidth() - imagebitmap[index].getWidth() - 5,
y + 3, imagebitmap[index].getWidth(),
imagebitmap[index].getHeight(), imagebitmap[index], 0, 0);
graphics.setColor(Color.WHITE);
graphics.drawRect(0, y, width, imagebitmap[index].getHeight() + 10);
graphics.setColor(Color.BLACK);
graphics.setFont(Font.getDefault().derive(Font.BOLD, 20));
graphics.drawText(title[index], 5, y + 3, 0, Display.getWidth()
- imagebitmap[index].getWidth() - 10);
System.out.println(Display.getWidth()
- imagebitmap[index].getWidth() - 10);
graphics.setColor(Color.GRAY);
graphics.setFont(Font.getDefault().derive(Font.BOLD, 15));
graphics.drawText(date[index], 5, y + 6
+ Font.getDefault().getHeight() + 3);
if (islatest) {
graphics.setColor(Color.RED);
graphics.setFont(Font.getDefault().derive(Font.BOLD, 15));
graphics.drawText(category[index], Font.getDefault()
.getAdvance(date[index]) + 3, y + 6
+ Font.getDefault().getHeight() + 3);
}
}
public Object get(ListField listField, int index) {
return content.elementAt(index);
}
public int getPreferredWidth(ListField listField) {
return Display.getWidth();
}
public int indexOfList(ListField listField, String prefix, int start) {
return content.indexOf(prefix, start);
}
}
public int getCurrentPosition() {
return currentPosition;
}
protected boolean navigationClick(int status, int time) {
int index = getCurrentPosition();
if (catsid[index] == 9) {
if (Config_GlobalFunction.isConnected()) {
webpage = new BrowserField();
listener = new Custom_BrowserFieldListener();
webpage.addListener(listener);
MainScreen aboutus = new Menu_Aboutus();
aboutus.add(webpage);
Main.getUiApplication().pushScreen(aboutus);
webpage.requestContent("http://www.orientaldaily.com.my/index.php?option=com_k2&view=item&id="
+ newsid[index] + ":&Itemid=223");
} else
Config_GlobalFunction.Message(Config_GlobalFunction.nowifi, 1);
} else
Main.getUiApplication().pushScreen(
new Main_NewsDetail(newsid[index]));
return true;
}
}

BackKey not working when a Thread is running. Why?

PageA Navigated to PageB
There is a thread is running for HttpWebRequest.
Back Key is invalid when the Thread is running.
PageB Code:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
Debug.WriteLine("OnNavigatedTo");
//base.OnNavigatedTo(e);
DoWork();
}
void DoWork()
{
t = new Thread(new ThreadStart(() =>
{
request = WebRequest.Create("http://www.google.com") as HttpWebRequest;
request.BeginGetResponse(new AsyncCallback(AsyncBack), request);
}));
t.IsBackground = true;
t.Start();
}
void AsyncBack(IAsyncResult ias)
{
HttpWebRequest req = (HttpWebRequest)ias.AsyncState;
using (HttpWebResponse res = req.EndGetResponse(ias) as HttpWebResponse)
{
this.Dispatcher.BeginInvoke(() =>
{
this.PageTitle.Text = res.ContentLength.ToString();
long length = res.ContentLength;
for (long i = 0; i < length; i++)
{
//here imitate a long time for working
Debug.WriteLine(i);
if (i == length)
{
break;
}
}
Debug.WriteLine(res.ContentLength);
});
}
}
the Back Key is invalid until Method AsyncBack() is done.
'Back Key is invalid' Is that the app is not back to PageA when you touch the Back Key until Method AsyncBack() Done.
Why? Help me?
Why the bloody hell are you wrapping a async request in a custom thread? That doesn't even remotely make sense.
Then again, your question doesn't make much sense either, but most likely the error is related to the request is attempting to invoke a operation, via. the dispatcher, on the wrong page.
In your code you block UI thread for a long time, so you can't navigate page back, because it happens also on UI thread, put into Dispatcher only code that can't be executed not on UI.
void DoWork()
{
HttpWebRequest request = WebRequest.Create("http://www.googl.com") as HttpWebRequest;
request.BeginGetResponse(new AsyncCallback(AsyncBack), request);
}
void AsyncBack(IAsyncResult ias)
{
HttpWebRequest req = (HttpWebRequest)ias.AsyncState;
using (HttpWebResponse res = req.EndGetResponse(ias) as HttpWebResponse)
{
this.Dispatcher.BeginInvoke(() =>
{
this.PageTitle.Text = res.ContentLength.ToString();
});
long length = res.ContentLength;
for (long i = 0; i < length; i++)
{
long i_ = i;
//here imitate a long time for working
Thread.Sleep(10);
this.Dispatcher.BeginInvoke(() =>
{
this.PageTitle.Text = i_.ToString();
});
if (i == length)
{
break;
}
}
Debug.WriteLine(res.ContentLength);
}
}

Resources